From a75c546171844b758fcf13ae5ede0591c1d7d6dc Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 21 Apr 2020 04:10:44 -0700 Subject: [PATCH 01/25] vimrc --- vim/.vimrc | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 vim/.vimrc diff --git a/vim/.vimrc b/vim/.vimrc new file mode 100644 index 0000000..93aa08d --- /dev/null +++ b/vim/.vimrc @@ -0,0 +1,244 @@ +" Basic +" set shell=/bin/zsh +set belloff=all " Annoying bell off +set encoding=utf-8 +set nocompatible " Set no compatible with vi +set noswapfile + +set hidden +set ruler +set laststatus=2 +set number +set nocursorline +set lazyredraw +set title "Show the filename in the window title bar +set nostartofline "Make j/k respect the columns + +set autoindent +set autoread +set backspace=indent,eol,start +set display+=lastline +set foldmethod=syntax +set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ +set nofoldenable +set completeopt-=preview "Disable the preview window for completions. +set expandtab +set smarttab +set splitbelow splitright "Pane splitting +set noshowmode "The status line will show the mode. +set nowrap +set nrformats-=octal +set scrolloff=1 +set sidescrolloff=5 + +set re=1 "Force the old regex engine on any version newer +set wildmenu +set wildmode=longest:full " displayes longest match first + +set shiftwidth=2 +set softtabstop=2 +set tabstop=2 + +set incsearch +set ignorecase +set smartcase + +let &t_SI.="\e[5 q" "SI = INSERT mode +let &t_EI.="\e[1 q" + +"Read/Write mappings +inoremap :w +nnoremap :w +inoremap :wq! +nnoremap :wq! +inoremap :qa! +nnoremap :qa! + +" Pressing Ctrl-u deletes text you've typed in the current line +" Ctrl-w deletes the word before the cursor, both undoable. +" This adds Ctrl-g first to start a new change +inoremap u +inoremap u +inoremap u + +" Clipboard Settings +noremap y "*y +noremap p "*p + +" Uunset highlighting on matches +nnoremap :nohlsearch + +" Exit terminal mode +tnoremap + +" Map buffer list +nnoremap b :ls:b + +" fzf fuzzy search +nmap t :Files +nmap r :Tags + +" Autoreload external changes +au FocusGained,BufEnter * :checktime + +" Session storage directories and viminfo +set viminfo='100,f1,<50,s10,h,n~/.config/nvim/viminfo +set backupdir=~/.config/nvim/.backup// +set directory=~/.config/nvim/.swap// + +" Find snake_case - '+', '_' to convert +:nnoremap + /\w\+_ +:nnoremap _ f_x~ + +"Execute %!python -m json.tool to format json +runtime! macros/matchit.vim + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Plugins +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +call plug#begin('~/.vim/plugged') + "Basics + Plug 'roxma/vim-hug-neovim-rpc' + Plug 'tpope/vim-eunuch' + Plug 'junegunn/vim-easy-align' +" (Optional) Multi-entry selection UI. + Plug 'junegunn/fzf' + Plug 'Chiel92/vim-autoformat' " vim autoformat + Plug 'w0rp/ale' + Plug 'tell-k/vim-autopep8' + Plug 'jiangmiao/auto-pairs' + Plug 'tpope/vim-surround' " selection surroundings + Plug 'scrooloose/nerdcommenter' " code commenting + "Visuals + Plug 'flazz/vim-colorschemes' + Plug 'ap/vim-css-color' " CSS Vim color preview + "Language-Specific (alphabetical by package-identifier + Plug 'chr4/nginx.vim' " nginx configs + Plug 'mxw/vim-jsx' " JSX highlighter (depends on underlying JS highlighter + Plug 'HerringtonDarkholme/yats.vim' + Plug 'mhartington/nvim-typescript', {'do': ':!install.sh \| UpdateRemotePlugins'} + + "FZF Fuzzy Search + Plug 'junegunn/fzf', { 'build': './install --all', 'merged': 0 } + Plug 'junegunn/fzf.vim', { 'depends': 'fzf' } + +call plug#end() + +nnoremap :call LanguageClient_contextMenu() +" Or map each action separately +" nnoremap K :call LanguageClient#textDocument_hover() +" nnoremap gd :call LanguageClient#textDocument_definition() +" nnoremap :call LanguageClient#textDocument_rename() + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Color Scheme +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +filetype plugin indent on +syntax enable +" This line enables the true color support. +let $NVIM_TUI_ENABLE_TRUE_COLOR=1 +set termguicolors +set background=dark + +let g:jellybeans_use_lowcolor_black = 1 +let g:jellybeans_overrides = { +\ 'Todo': { 'guifg': '303030', 'guibg': 'f0f000', +\ 'ctermfg': 'Black', 'ctermbg': 'Yellow', +\ 'attr': 'bold' }, +\ 'Comment': { 'guifg': 'cccccc' }, +\} + +let g:jellybeans_overrides = { +\ 'MatchParen': { 'guifg': 'dd0093', 'guibg': '000000', +\ 'ctermfg': 'Magenta', 'ctermbg': '' }, +\} +colorscheme jellybeans + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Autopair Completion +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let g:AutoPairsFlyMode = 0 +let g:AutoPairsShortcutBackInsert = '' +let g:AutoPairsShortcutJump = 0 + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Lightline +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let g:lightline = { + \ 'colorscheme': 'powerline', + \ 'active': { + \ 'left': [ [ 'mode', 'paste' ], + \ [ 'readonly', 'absolutepath', 'modified' ] ], + \ 'right': [ [ 'custom-lineinfo' ], + \ [ 'custom-fileinfo' ] ], + \ }, + \ 'inactive': { + \ 'left': [ [ 'readonly', 'filename', 'modified' ] ], + \ 'right': [ [ 'custom-lineinfo' ], + \ [ 'custom-fileinfo' ] ], + \ }, + \ 'component': { + \ 'custom-lineinfo': ' %3p%% ┃ %4l/%L :%3c', + \ }, + \ 'component_function': { + \ 'custom-fileinfo': 'LightlineFileInfo', + \ 'readonly': 'LightlineReadonly', + \ }, + \ 'tabline': { + \ 'left': [ [ 'tabs' ] ], + \ 'right': [ ], + \ }, +\ } + +function! LightlineReadonly() + return &readonly ? '∄' : '' +endfunction +function! LightlineFileInfo() + let displayFiletype = &filetype !=# '' ? &filetype : 'no ft' + return ' ' . displayFiletype . ' ┃ ' . &fileencoding . '[' . &fileformat . '] ' +endfunction + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Completions +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Completions +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +"Autocomplete and cycle from top-to-bottom of suggestions using . +inoremap pumvisible() ? "\" : "\" + +": completion back. +inoremap pumvisible() ? "\" : "\" +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Python Autopep 8 Formatting +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let g:autopep8_max_line_length=79 +let g:autopep8_ignore="E501,W293,C0321" +let g:autopep8_disable_show_diff=0 +let g:autopep8_on_save=1 " Autosave on save + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Automatic Whitespace Trimming and Formatting (for select filetypes) +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let g:ale_typescript_tslint_ignore_empty_files = 0 +let g:ale_typescript_tsserver_config_path = '' +let g:ale_typescript_tsserver_executable = 'tsserver' +let g:ale_typescript_tsserver_use_global = 0 + +let g:jsx_ext_required = 0 "jsx highlighting + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Ale Syntax & Language Specific +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let g:ale_sign_error = '✘' +let g:ale_sign_warning = '⚠' +let g:ale_fix_on_save = 1 +let g:ale_fixers = { +\ '*': ['remove_trailing_lines', 'trim_whitespace'], +\ 'javascript': ['prettier', 'eslint'], +\ 'typescript': ['prettier', 'tslint'], +\} +highlight ALEErrorSign ctermbg=NONE ctermfg=red +highlight ALEWarningSign ctermbg=NONE ctermfg=yellow From 98f6f57e74f2b788867842dfe1e69537176a41e7 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 25 Oct 2020 17:24:51 -0700 Subject: [PATCH 02/25] fix paths --- .gitignore | 6 +++--- bash/bash_profile | 5 ++++- bash/bashrc | 8 ++++++++ bash/profile | 6 ++++++ bash/scripts/aliases | 3 ++- bash/scripts/prompt | 9 ++++++++- install | 5 +++-- 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 70ce1bc..300ef90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .DS_STORE history/* - ./bash_* +/bash_* - /sandbox - scripts/.fzf* +/sandbox +scripts/.fzf* diff --git a/bash/bash_profile b/bash/bash_profile index 27851e7..a489e2f 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -3,7 +3,6 @@ # Set environment variables and startup programs here. echo "$(bash --version | head -2)" -echo "akljdlkajdlk $DIR" shopt -q login_shell && echo -e "\nSourced .bash_profile" || echo -e "\nSourced .profile" if [[ -n $SSH_CONNECTION ]]; then @@ -13,3 +12,7 @@ else fi [[ -f "$HOME/.bashrc" ]] && source "$HOME/.bashrc" +export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" +export MANPATH="/home/linuxbrew/.linuxbrew/share/man:$MANPATH" +export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:$INFOPATH" +export PATH="$PATH:/opt/yarn-[version]/bin" diff --git a/bash/bashrc b/bash/bashrc index 5e809a8..90490e4 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -34,5 +34,13 @@ export LESSOPEN="|/usr/local/bin/lesspipe.sh %s" LESS_ADVANCED_PREPROCESSOR=1 # [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" export PATH="$PATH:/usr/local/bin/" +export PATH="$PATH:$HOME/.npm-packages/bin" + [ -f ~/.go ] && source ~/.go [ -f ~/.fzf.bash ] && source ~/.fzf.bash +export PATH=/home/laujonat/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +#export DOCKER_HOST=tcp://localhost:2375 diff --git a/bash/profile b/bash/profile index cafcf42..10235f4 100644 --- a/bash/profile +++ b/bash/profile @@ -10,3 +10,9 @@ export SSH_AUTH_SOCK=~/.ssh/ssh-agent.sock ssh-add -l 2>/dev/null >/dev/null # if not valid, then start ssh-agent using $SSH_AUTH_SOCK [ $? -ge 2 ] && ssh-agent -a "$SSH_AUTH_SOCK" >/dev/null + +if [ -d "$HOME/bin" ] ; then + PATH="$PATH:$HOME/bin" +fi +PATH="$HOME/.npm-global/bin:$PATH" +PATH="$HOME/.npm-global/bin:$PATH" diff --git a/bash/scripts/aliases b/bash/scripts/aliases index c68889d..24b6dd4 100644 --- a/bash/scripts/aliases +++ b/bash/scripts/aliases @@ -6,7 +6,7 @@ alias vim=nvim fi dir="$(pwd)" -alias als="vim $dir/aliases" +alias als="vim $(dir)/.aliases" alias afind='ack -il' alias history='fc -l 1' alias l='ls -lah' @@ -20,6 +20,7 @@ alias pu='pushd' alias rd='rmdir' alias cp='cp -i' alias mv='mv -i' +alias algs='cd ~/Programs/algoRepl' # Search alias grep='grep $grep_opts' diff --git a/bash/scripts/prompt b/bash/scripts/prompt index 5afaf91..29020b6 100644 --- a/bash/scripts/prompt +++ b/bash/scripts/prompt @@ -53,4 +53,11 @@ export CLICOLOR=1 export LS_COLORS='di=36:fi=0:ln=94:pi=0:so=5:bd=0:cd=0:or=91:mi=31:ex=92:*.rpm=90' -export PS1="[${blu}\u${red}@${grn}\\h:${nc}\W]${light_cyn}$(parse_git_branch)${nc} " +export PS1="[${blu}\u${red}@${grn}\\h:${nc}\W]${light_cyn}$(git_current_branch)${nc} " +update_PS1 () { + PS1="[${blu}\u${red}@${grn}\\h:${nc}\W]${light_cyn}$(git_current_branch)${nc} " +} + +shopt -u promptvars +PROMPT_COMMAND=update_PS1 + diff --git a/install b/install index 28bd9e5..56ba32e 100755 --- a/install +++ b/install @@ -5,10 +5,11 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" echo "Repository is located at $DIR" cd $DIR export REPO="`git rev-parse --show-toplevel`" +export BASH_DIR=$REPO/bash +export SCRIPTS=$BASH_DIR/scripts function init() { - BASH_DIR=$REPO/bash - SCRIPTS=$BASH_DIR/scripts + echo $SCRIPTS BASH_SRC=bash_"$(date +%s)" touch -f $BASH_SRC From d0ff5959bfc25d411676b024727b12ad74082fdb Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 6 Dec 2020 00:13:12 -0800 Subject: [PATCH 03/25] add ssh agent to bp --- bash/bash_profile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/bash/bash_profile b/bash/bash_profile index a489e2f..6d80c12 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -16,3 +16,15 @@ export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH" export MANPATH="/home/linuxbrew/.linuxbrew/share/man:$MANPATH" export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:$INFOPATH" export PATH="$PATH:/opt/yarn-[version]/bin" + +export ZEPHYR_TOOLCHAIN_VARIANT="gnuarmemb" +export GNUARMEMB_TOOLCHAIN_PATH="/home/laujonat/hakkei/zmk-config/config" + +# Added by serverless binary installer +export PATH="$HOME/.serverless/bin:$PATH" +SSHAGENT=/usr/bin/ssh-agent +SSHAGENTARGS="-s" +if [ -z "$SSH_AUTH_SOCK" -a -x "$SSHAGENT" ]; then + eval `$SSHAGENT $SSHAGENTARGS` + trap "kill $SSH_AGENT_PID" 0 +fi From c4666dc189753c4a1f0beed2d16e7db40d9aa87e Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 21 Apr 2021 23:30:53 -0700 Subject: [PATCH 04/25] go path --- bash/bash_profile | 8 ++++---- bash/bashrc | 14 +++++++++++++- bash/profile | 7 ++++++- bash/scripts/prompt | 11 ++++++++--- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/bash/bash_profile b/bash/bash_profile index 6d80c12..7e3a7fb 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -17,13 +17,13 @@ export MANPATH="/home/linuxbrew/.linuxbrew/share/man:$MANPATH" export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:$INFOPATH" export PATH="$PATH:/opt/yarn-[version]/bin" -export ZEPHYR_TOOLCHAIN_VARIANT="gnuarmemb" -export GNUARMEMB_TOOLCHAIN_PATH="/home/laujonat/hakkei/zmk-config/config" +# export ZEPHYR_TOOLCHAIN_VARIANT="gnuarmemb" +export GNUARMEMB_TOOLCHAIN_PATH="/home/laujonat/gcc-arm-none-eabi-10-2020-q4-major" # Added by serverless binary installer export PATH="$HOME/.serverless/bin:$PATH" -SSHAGENT=/usr/bin/ssh-agent -SSHAGENTARGS="-s" +export SSHAGENT=/usr/bin/ssh-agent +export SSHAGENTARGS="-s" if [ -z "$SSH_AUTH_SOCK" -a -x "$SSHAGENT" ]; then eval `$SSHAGENT $SSHAGENTARGS` trap "kill $SSH_AGENT_PID" 0 diff --git a/bash/bashrc b/bash/bashrc index 90490e4..40c6d6a 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -33,9 +33,9 @@ fi export LESSOPEN="|/usr/local/bin/lesspipe.sh %s" LESS_ADVANCED_PREPROCESSOR=1 # [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" + export PATH="$PATH:/usr/local/bin/" export PATH="$PATH:$HOME/.npm-packages/bin" - [ -f ~/.go ] && source ~/.go [ -f ~/.fzf.bash ] && source ~/.fzf.bash export PATH=/home/laujonat/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin @@ -44,3 +44,15 @@ export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion #export DOCKER_HOST=tcp://localhost:2375 + +# tabtab source for packages +# uninstall by removing these lines +[ -f ~/.config/tabtab/__tabtab.bash ] && . ~/.config/tabtab/__tabtab.bash || true +export ZEPHYR_TOOLCHAIN_VARIANT=zephyr +export ZEPHYR_SDK_INSTALL_DIR=/home/laujonat/.local/zephyr-sdk-0.11.2 + + +export GOROOT=/usr/local/go +export GOPATH=$HOME/go +export PATH=$PATH:$GOROOT/bin:$GOPATH/bin +PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting diff --git a/bash/profile b/bash/profile index 10235f4..7dc8589 100644 --- a/bash/profile +++ b/bash/profile @@ -14,5 +14,10 @@ ssh-add -l 2>/dev/null >/dev/null if [ -d "$HOME/bin" ] ; then PATH="$PATH:$HOME/bin" fi + PATH="$HOME/.npm-global/bin:$PATH" -PATH="$HOME/.npm-global/bin:$PATH" + + +GOPATH="$HOME/.go" +export GOPATH +PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting diff --git a/bash/scripts/prompt b/bash/scripts/prompt index 29020b6..b445dec 100644 --- a/bash/scripts/prompt +++ b/bash/scripts/prompt @@ -53,11 +53,16 @@ export CLICOLOR=1 export LS_COLORS='di=36:fi=0:ln=94:pi=0:so=5:bd=0:cd=0:or=91:mi=31:ex=92:*.rpm=90' -export PS1="[${blu}\u${red}@${grn}\\h:${nc}\W]${light_cyn}$(git_current_branch)${nc} " update_PS1 () { - PS1="[${blu}\u${red}@${grn}\\h:${nc}\W]${light_cyn}$(git_current_branch)${nc} " -} + + export PS1="[${blu}\u${red}@${grn}\\h:${nc}\\W]${light_cyn}$(git_current_branch)${nc}\\n>> " + + # export PS1="\[\u@\h \W]@$(git_current_branch)${nc}$\\n" + + # export PS1="\[${blu}\u${red}@${grn}\\h:${nc}\W\]${light_cyn}$(git_current_branch)${nc}\" + # export PS1="\[\e[33;41m\][\[\e[m\]\[\e[32m\]\u\[\e[m\]\[\e[36m\]@\[\e[m\]\[\e[34m\]\h\[\e[m\]\[\e[33;41m\]]\[\e[m\]${git_current_branch}${nc}${ex} " +} shopt -u promptvars PROMPT_COMMAND=update_PS1 From 54347723551cb70fa91390ac0811a0377c556961 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 21 Apr 2021 23:31:02 -0700 Subject: [PATCH 05/25] update prompt wsl --- scripts/ps1_prompt | 174 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 scripts/ps1_prompt diff --git a/scripts/ps1_prompt b/scripts/ps1_prompt new file mode 100644 index 0000000..e0d8bbd --- /dev/null +++ b/scripts/ps1_prompt @@ -0,0 +1,174 @@ +T SYMBOLS +# s (yellow) -- stashed objects +# u (red) -- untracked files +# u (yellow) -- uncommitted changes +# x (red) -- HEAD isn't a valid object +# s (green) -- changes staged + +### COLORS + +# Reset +Color_Off='\[\e[0m\]' # Text Reset + +# Regular Colors +Black='\[\e[0;30m\]' # Black +Red='\[\e[0;31m\]' # Red +Green='\[\e[0;32m\]' # Green +Yellow='\[\e[0;33m\]' # Yellow +Blue='\[\e[0;34m\]' # Blue +Purple='\[\e[0;35m\]' # Purple +Cyan='\[\e[0;36m\]' # Cyan +White='\[\e[0;37m\]' # White + +# Bold +BBlack='\[\e[1;30m\]' # Black +BRed='\[\e[1;31m\]' # Red +BGreen='\[\e[1;32m\]' # Green +BYellow='\[\e[1;33m\]' # Yellow +BBlue='\[\e[1;34m\]' # Blue +BPurple='\[\e[1;35m\]' # Purple +BCyan='\[\e[1;36m\]' # Cyan +BWhite='\[\e[1;37m\]' # White + +# Underline +UBlack='\[\e[4;30m\]' # Black +URed='\[\e[4;31m\]' # Red +UGreen='\[\e[4;32m\]' # Green +UYellow='\[\e[4;33m\]' # Yellow +UBlue='\[\e[4;34m\]' # Blue +UPurple='\[\e[4;35m\]' # Purple +UCyan='\[\e[4;36m\]' # Cyan +UWhite='\[\e[4;37m\]' # White + +# Background +On_Black='\[\e[40m\]' # Black +On_Red='\[\e[41m\]' # Red +On_Green='\[\e[42m\]' # Green +On_Yellow='\[\e[43m\]' # Yellow +On_Blue='\[\e[44m\]' # Blue +On_Purple='\[\e[45m\]' # Purple +On_Cyan='\[\e[46m\]' # Cyan +On_White='\[\e[47m\]' # White + +# High Intensity +IBlack='\[\e[0;90m\]' # Black +IRed='\[\e[0;91m\]' # Red +IGreen='\[\e[0;92m\]' # Green +IYellow='\[\e[0;93m\]' # Yellow +IBlue='\[\e[0;94m\]' # Blue +IPurple='\[\e[0;95m\]' # Purple +ICyan='\[\e[0;96m\]' # Cyan +IWhite='\[\e[0;97m\]' # White + +# Bold High Intensity +BIBlack='\[\e[1;90m\]' # Black +BIRed='\[\e[1;91m\]' # Red +BIGreen='\[\e[1;92m\]' # Green +BIYellow='\[\e[1;93m\]' # Yellow +BIBlue='\[\e[1;94m\]' # Blue +BIPurple='\[\e[1;95m\]' # Purple +BICyan='\[\e[1;96m\]' # Cyan +BIWhite='\[\e[1;97m\]' # White + +# High Intensity backgrounds +On_IBlack='\[\e[0;100m\]' # Black +On_IRed='\[\e[0;101m\]' # Red +On_IGreen='\[\e[0;102m\]' # Green +On_IYellow='\[\e[0;103m\]' # Yellow +On_IBlue='\[\e[0;104m\]' # Blue +On_IPurple='\[\e[0;105m\]' # Purple +On_ICyan='\[\e[0;106m\]' # Cyan +On_IWhite='\[\e[0;107m\]' # White + +### SYMBOL DEFINITIONS + +GIT_PS1_SHOWDIRTYSTATE=1 +GIT_PS1_SHOWSTASHSTATE=1 +GIT_PS1_SHOWUNTRACKEDFILES=1 +GIT_PS1_STASHED_INDICATOR="${Yellow}s${Color_Off}" +GIT_PS1_UNTRACKED_INDICATOR="${Red}u${Color_Off}" +GIT_PS1_UNCOMMITTED_INDICATOR="${Yellow}u${Color_Off}" +GIT_PS1_BOMB_INDICATOR="${Red}x${Color_Off}" +GIT_PS1_STAGED_INDICATOR="${Green}s${Color_Off}" + +### WORKING DIRECTORY +WorkingDirectory="\w" + +### GIT PROMPT +my_git_prompt() +{ + GitPrompt="\w" + AreWeInRepository=$(__gitdir) + + if [[ -n $AreWeInRepository ]]; then + WorkingRepositoryName=$(git rev-parse --show-toplevel | awk -F/ '{print $NF}') + WorkingRepositoryCwd=$(git rev-parse --show-prefix) + WorkingBranch="" + UntrackedIndicator="" + StashIndicator="" + UncommittedIndicator="" + StagedIndicator="" + GitStatus="" + + b="$(git symbolic-ref HEAD 2>/dev/null)" || { + + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --tags --exact-match HEAD ;; + esac 2>/dev/null)" || + + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || + b="unknown" + b="($b)" + } + WorkingBranch="${b##refs/heads/}" + + + git rev-parse --verify refs/stash >/dev/null 2>&1 && StashIndicator=$GIT_PS1_STASHED_INDICATOR + + if [ -n "$(cd `__gitdir` && cd .. && git ls-files --others --exclude-standard)" ]; then + UntrackedIndicator=$GIT_PS1_UNTRACKED_INDICATOR + fi + + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then + git diff --no-ext-diff --quiet --exit-code || UncommittedIndicator=$GIT_PS1_UNCOMMITTED_INDICATOR + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet HEAD -- || StagedIndicator=$GIT_PS1_STAGED_INDICATOR + else + StagedIndicator=$GIT_PS1_BOMB_INDICATOR + fi + fi + + if [[ -n $StagedIndicator || -n $StashIndicator || -n $UncommittedIndicator || -n $UntrackedIndicator ]] + then + GitStatus="(" + + [[ -n $StagedIndicator ]] && GitStatus="${GitStatus}${GIT_PS1_STAGED_INDICATOR}" + [[ -n $StashIndicator ]] && GitStatus="${GitStatus}${GIT_PS1_STASHED_INDICATOR}" + [[ -n $UncommittedIndicator ]] && GitStatus="${GitStatus}${GIT_PS1_UNCOMMITTED_INDICATOR}" + [[ -n $UntrackedIndicator ]] && GitStatus="${GitStatus}${GIT_PS1_UNTRACKED_INDICATOR}" + + GitStatus="${GitStatus})" + else + GitStatus="(${Green}+${Color_Off})" + fi + + if [[ -z ${WorkingRepositoryCwd} ]] + then + WorkingRepositoryCwd="/" + fi + + GitPrompt="\n(${BGreen}Git${Color_Off})[${BPurple}${WorkingRepositoryName}${Color_Off}] ${BYellow}${WorkingRepositoryCwd}${Color_Off}\n(${BIWhite}${WorkingBranch}${Color_Off})${GitStatus}" + fi + + echo $GitPrompt +} + +PROMPT_COMMAND='PS1="${BBlack}[\u@\h]${Color_Off}$(my_git_prompt)\$ "' From 26f6d2ba6c28204ebebf9608ec93026a7627c341 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 4 May 2021 16:46:44 -0700 Subject: [PATCH 06/25] update profile --- .gitignore | 1 + bash/profile | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 300ef90..4e04ee3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ history/* /sandbox scripts/.fzf* +bash/scripts/.nvimlog diff --git a/bash/profile b/bash/profile index 7dc8589..59214d6 100644 --- a/bash/profile +++ b/bash/profile @@ -21,3 +21,5 @@ PATH="$HOME/.npm-global/bin:$PATH" GOPATH="$HOME/.go" export GOPATH PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting + +PATH="$PATH:/mnt/c/Users/jonas/AppData/Local/Programs/Microsoft VS Code" From e5e85d4edfa096c43d4229cab99c2d12c908d25b Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 7 Jun 2021 02:22:54 -0700 Subject: [PATCH 07/25] bsh gcp --- bash/bashrc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bash/bashrc b/bash/bashrc index 40c6d6a..7a9846c 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -51,8 +51,13 @@ export NVM_DIR="$HOME/.nvm" export ZEPHYR_TOOLCHAIN_VARIANT=zephyr export ZEPHYR_SDK_INSTALL_DIR=/home/laujonat/.local/zephyr-sdk-0.11.2 - export GOROOT=/usr/local/go export GOPATH=$HOME/go export PATH=$PATH:$GOROOT/bin:$GOPATH/bin PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting + +# The next line updates PATH for the Google Cloud SDK. +if [ -f '/home/laujonat/google-cloud-sdk/path.bash.inc' ]; then . '/home/laujonat/google-cloud-sdk/path.bash.inc'; fi + +# The next line enables shell command completion for gcloud. +if [ -f '/home/laujonat/google-cloud-sdk/completion.bash.inc' ]; then . '/home/laujonat/google-cloud-sdk/completion.bash.inc'; fi From 04a3fd675ed298359c83f04564cf484704adeb29 Mon Sep 17 00:00:00 2001 From: laujonat Date: Mon, 12 Jul 2021 17:02:01 -0700 Subject: [PATCH 08/25] tmux --- bash/bash_profile | 2 +- bash/bashrc | 10 +- bash/profile | 2 +- tmux/.gitattributes | 7 + tmux/.gitignore | 1 + tmux/.tmux.conf | 1447 +++++++++++++++++++++++++++++++++++++++++ tmux/.tmux.conf.local | 406 ++++++++++++ 7 files changed, 1869 insertions(+), 6 deletions(-) create mode 100644 tmux/.gitattributes create mode 100644 tmux/.gitignore create mode 100644 tmux/.tmux.conf create mode 100644 tmux/.tmux.conf.local diff --git a/bash/bash_profile b/bash/bash_profile index 7e3a7fb..94ea667 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -18,7 +18,7 @@ export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:$INFOPATH" export PATH="$PATH:/opt/yarn-[version]/bin" # export ZEPHYR_TOOLCHAIN_VARIANT="gnuarmemb" -export GNUARMEMB_TOOLCHAIN_PATH="/home/laujonat/gcc-arm-none-eabi-10-2020-q4-major" +export GNUARMEMB_TOOLCHAIN_PATH="/home/tlcie/gcc-arm-none-eabi-10-2020-q4-major" # Added by serverless binary installer export PATH="$HOME/.serverless/bin:$PATH" diff --git a/bash/bashrc b/bash/bashrc index 7a9846c..1547ec4 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -38,7 +38,7 @@ export PATH="$PATH:/usr/local/bin/" export PATH="$PATH:$HOME/.npm-packages/bin" [ -f ~/.go ] && source ~/.go [ -f ~/.fzf.bash ] && source ~/.fzf.bash -export PATH=/home/laujonat/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +export PATH=/home/tlcie/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm @@ -49,7 +49,7 @@ export NVM_DIR="$HOME/.nvm" # uninstall by removing these lines [ -f ~/.config/tabtab/__tabtab.bash ] && . ~/.config/tabtab/__tabtab.bash || true export ZEPHYR_TOOLCHAIN_VARIANT=zephyr -export ZEPHYR_SDK_INSTALL_DIR=/home/laujonat/.local/zephyr-sdk-0.11.2 +export ZEPHYR_SDK_INSTALL_DIR=/home/tlcie/.local/zephyr-sdk-0.11.2 export GOROOT=/usr/local/go export GOPATH=$HOME/go @@ -57,7 +57,9 @@ export PATH=$PATH:$GOROOT/bin:$GOPATH/bin PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting # The next line updates PATH for the Google Cloud SDK. -if [ -f '/home/laujonat/google-cloud-sdk/path.bash.inc' ]; then . '/home/laujonat/google-cloud-sdk/path.bash.inc'; fi +# if [ -f '/home/tlcie/google-cloud-sdk/path.bash.inc' ]; then . '/home/tlcie/google-cloud-sdk/path.bash.inc'; fi # The next line enables shell command completion for gcloud. -if [ -f '/home/laujonat/google-cloud-sdk/completion.bash.inc' ]; then . '/home/laujonat/google-cloud-sdk/completion.bash.inc'; fi +# if [ -f '/home/tlcie/google-cloud-sdk/completion.bash.inc' ]; then . '/home/tlcie/google-cloud-sdk/completion.bash.inc'; fi + +export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH" diff --git a/bash/profile b/bash/profile index 59214d6..3c0488b 100644 --- a/bash/profile +++ b/bash/profile @@ -22,4 +22,4 @@ GOPATH="$HOME/.go" export GOPATH PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting -PATH="$PATH:/mnt/c/Users/jonas/AppData/Local/Programs/Microsoft VS Code" +PATH="$PATH:/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code" diff --git a/tmux/.gitattributes b/tmux/.gitattributes new file mode 100644 index 0000000..8e06491 --- /dev/null +++ b/tmux/.gitattributes @@ -0,0 +1,7 @@ +* text=auto + +# git +.gitignore text + +.tmux.conf eol=lf +.tmux.conf.local eol=lf diff --git a/tmux/.gitignore b/tmux/.gitignore new file mode 100644 index 0000000..9bfd626 --- /dev/null +++ b/tmux/.gitignore @@ -0,0 +1 @@ +plugins/** diff --git a/tmux/.tmux.conf b/tmux/.tmux.conf new file mode 100644 index 0000000..8e7b05a --- /dev/null +++ b/tmux/.tmux.conf @@ -0,0 +1,1447 @@ +# : << EOF +# https://github.com/gpakosz/.tmux +# (‑●‑●)> dual licensed under the WTFPL v2 license and the MIT license, +# without any warranty. +# Copyright 2012— Gregory Pakosz (@gpakosz). +# /!\ do not edit this file +# instead, override settings in ~/.tmux.conf.local, see README.md + + +# -- general ------------------------------------------------------------------- + +set -g default-terminal "screen-256color" # colors! +setw -g xterm-keys on +set -s escape-time 10 # faster command sequences +set -sg repeat-time 600 # increase repeat timeout +set -s focus-events on + +set -g prefix2 C-a # GNU-Screen compatible prefix +bind C-a send-prefix -2 + +set -q -g status-utf8 on # expect UTF-8 (tmux < 2.2) +setw -q -g utf8 on + +set -g history-limit 5000 # boost history + +# edit configuration +bind e new-window -n "~/.tmux.conf.local" "EDITOR=\${EDITOR//mvim/vim} && EDITOR=\${EDITOR//gvim/vim} && \${EDITOR:-vim} ~/.tmux.conf.local && tmux source ~/.tmux.conf && tmux display \"~/.tmux.conf sourced\"" + +# reload configuration +bind r source-file ~/.tmux.conf \; display '~/.tmux.conf sourced' + + +# -- display ------------------------------------------------------------------- + +set -g base-index 1 # start windows numbering at 1 +setw -g pane-base-index 1 # make pane numbering consistent with windows + +setw -g automatic-rename on # rename window to reflect current program +set -g renumber-windows on # renumber windows when a window is closed + +set -g set-titles on # set terminal title + +set -g display-panes-time 800 # slightly longer pane indicators display time +set -g display-time 1000 # slightly longer status messages display time + +set -g status-interval 10 # redraw status line every 10 seconds + +# clear both screen and history +bind -n C-l send-keys C-l \; run 'sleep 0.1' \; clear-history + +# activity +set -g monitor-activity on +set -g visual-activity off + + +# -- navigation ---------------------------------------------------------------- + +# create session +bind C-c new-session + +# find session +bind C-f command-prompt -p find-session 'switch-client -t %%' + +# split current window horizontally +bind - split-window -v +# split current window vertically +bind _ split-window -h + +# pane navigation +bind -r h select-pane -L # move left +bind -r j select-pane -D # move down +bind -r k select-pane -U # move up +bind -r l select-pane -R # move right +bind > swap-pane -D # swap current pane with the next one +bind < swap-pane -U # swap current pane with the previous one + +# maximize current pane +bind + run 'cut -c3- ~/.tmux.conf | sh -s _maximize_pane "#{session_name}" #D' + +# pane resizing +bind -r H resize-pane -L 2 +bind -r J resize-pane -D 2 +bind -r K resize-pane -U 2 +bind -r L resize-pane -R 2 + +# window navigation +unbind n +unbind p +bind -r C-h previous-window # select previous window +bind -r C-l next-window # select next window +bind Tab last-window # move to last active window + +# toggle mouse +bind m run "cut -c3- ~/.tmux.conf | sh -s _toggle_mouse" + + +# -- urlview ------------------------------------------------------------------- + +bind U run "cut -c3- ~/.tmux.conf | sh -s _urlview #{pane_id}" + + +# -- facebook pathpicker ------------------------------------------------------- + +bind F run "cut -c3- ~/.tmux.conf | sh -s _fpp #{pane_id}" + + +# -- list choice (tmux < 2.4) -------------------------------------------------- + +# vi-choice is gone in tmux >= 2.4 +run -b 'tmux bind -t vi-choice h tree-collapse 2> /dev/null || true' +run -b 'tmux bind -t vi-choice l tree-expand 2> /dev/null || true' +run -b 'tmux bind -t vi-choice K start-of-list 2> /dev/null || true' +run -b 'tmux bind -t vi-choice J end-of-list 2> /dev/null || true' +run -b 'tmux bind -t vi-choice H tree-collapse-all 2> /dev/null || true' +run -b 'tmux bind -t vi-choice L tree-expand-all 2> /dev/null || true' +run -b 'tmux bind -t vi-choice Escape cancel 2> /dev/null || true' + + +# -- edit mode (tmux < 2.4) ---------------------------------------------------- + +# vi-edit is gone in tmux >= 2.4 +run -b 'tmux bind -ct vi-edit H start-of-line 2> /dev/null || true' +run -b 'tmux bind -ct vi-edit L end-of-line 2> /dev/null || true' +run -b 'tmux bind -ct vi-edit q cancel 2> /dev/null || true' +run -b 'tmux bind -ct vi-edit Escape cancel 2> /dev/null || true' + + +# -- copy mode ----------------------------------------------------------------- + +bind Enter copy-mode # enter copy mode + +run -b 'tmux bind -t vi-copy v begin-selection 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi v send -X begin-selection 2> /dev/null || true' +run -b 'tmux bind -t vi-copy C-v rectangle-toggle 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi C-v send -X rectangle-toggle 2> /dev/null || true' +run -b 'tmux bind -t vi-copy y copy-selection 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi y send -X copy-selection-and-cancel 2> /dev/null || true' +run -b 'tmux bind -t vi-copy Escape cancel 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi Escape send -X cancel 2> /dev/null || true' +run -b 'tmux bind -t vi-copy H start-of-line 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi H send -X start-of-line 2> /dev/null || true' +run -b 'tmux bind -t vi-copy L end-of-line 2> /dev/null || true' +run -b 'tmux bind -T copy-mode-vi L send -X end-of-line 2> /dev/null || true' + +# copy to X11 clipboard +if -b 'command -v xsel > /dev/null 2>&1' 'bind y run -b "tmux save-buffer - | xsel -i -b"' +if -b '! command -v xsel > /dev/null 2>&1 && command -v xclip > /dev/null 2>&1' 'bind y run -b "tmux save-buffer - | xclip -i -selection clipboard >/dev/null 2>&1"' +# copy to macOS clipboard +if -b 'command -v pbcopy > /dev/null 2>&1' 'bind y run -b "tmux save-buffer - | pbcopy"' +if -b 'command -v reattach-to-user-namespace > /dev/null 2>&1' 'bind y run -b "tmux save-buffer - | reattach-to-user-namespace pbcopy"' +# copy to Windows clipboard +if -b 'command -v clip.exe > /dev/null 2>&1' 'bind y run -b "tmux save-buffer - | clip.exe"' +if -b '[ -c /dev/clipboard ]' 'bind y run -b "tmux save-buffer - > /dev/clipboard"' + + +# -- buffers ------------------------------------------------------------------- + +bind b list-buffers # list paste buffers +bind p paste-buffer # paste from the top paste buffer +bind P choose-buffer # choose which buffer to paste from + + +# -- user defined overrides ---------------------------------------------------- + +source -q ~/.tmux.conf.local + + +# -- 8< ------------------------------------------------------------------------ + +run 'cut -c3- ~/.tmux.conf | sh -s _apply_configuration' + + +# EOF +# +# # exit the script if any statement returns a non-true return value +# set -e +# +# unset GREP_OPTIONS +# export LC_NUMERIC=C +# +# if ! printf '' | sed -E 's///' 2>/dev/null; then +# if printf '' | sed -r 's///' 2>/dev/null; then +# sed () { +# n=$#; while [ "$n" -gt 0 ]; do arg=$1; shift; case $arg in -E*) arg=-r${arg#-E};; esac; set -- "$@" "$arg"; n=$(( n - 1 )); done +# command sed "$@" +# } +# fi +# fi +# +# _uname_s=$(uname -s) +# +# _tmux_version=$(tmux -V | awk '{gsub(/[^0-9.]/, "", $2); print ($2+0) * 100}') +# +# _is_enabled() { +# [ x"$1" = x"true" ] || [ x"$1" = x"yes" ] || [ x"$1" = x"enabled" ] || [ x"$1" = x"1" ] +# } +# +# _circled() { +# circled_digits='⓪ ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳' +# if [ "$1" -le 20 ] 2>/dev/null; then +# i=$(( $1 + 1 )) +# eval set -- "$circled_digits" +# eval echo "\${$i}" +# else +# echo "$1" +# fi +# } +# +# _decode_unicode_escapes() { +# printf '%s' "$*" | perl -CS -pe 's/(\\u([0-9A-Fa-f]{1,4})|\\U([0-9A-Fa-f]{1,8}))/chr(hex($2.$3))/eg' 2>/dev/null +# } +# +# if command -v pkill > /dev/null 2>&1; then +# _pkillf() { +# pkill -f "$@" || true +# } +# else +# case "$_uname_s" in +# *CYGWIN*) +# _pkillf() { +# while IFS= read -r pid; do +# kill "$pid" || true +# done << EOF +# $(grep -Eao "$@" /proc/*/cmdline | xargs -0 | sed -E -n 's,/proc/([0-9]+)/.+$,\1,pg') +# EOF +# } +# ;; +# *) +# _pkillf() { +# while IFS= read -r pid; do +# kill "$pid" || true +# done << EOF +# $(ps -x -o pid= -o command= | grep -E "$@" | cut -d' ' -f1) +# EOF +# } +# ;; +# esac +# fi +# +# _maximize_pane() { +# current_session=${1:-$(tmux display -p '#{session_name}')} +# current_pane=${2:-$(tmux display -p '#{pane_id}')} +# +# dead_panes=$(tmux list-panes -s -t "$current_session" -F '#{pane_dead} #{pane_id} #{pane_start_command}' | grep -E -o '^1 %.+maximized.+$' || true) +# restore=$(printf "%s" "$dead_panes" | sed -n -E -e "s/^1 $current_pane .+maximized.+'(%[0-9]+)'\"?$/tmux swap-pane -s \1 -t $current_pane \; kill-pane -t $current_pane/p"\ +# -e "s/^1 (%[0-9]+) .+maximized.+'$current_pane'\"?$/tmux swap-pane -s \1 -t $current_pane \; kill-pane -t \1/p") +# +# if [ -z "$restore" ]; then +# [ "$(tmux list-panes -t "$current_session:" | wc -l | sed 's/^ *//g')" -eq 1 ] && tmux display "Can't maximize with only one pane" && return +# current_pane_height=$(tmux display -t "$current_pane" -p "#{pane_height}") +# info=$(tmux new-window -t "$current_session:" -F "#{session_name}:#{window_index}.#{pane_id}" -P "maximized... 2>/dev/null & tmux setw -t \"$current_session:\" remain-on-exit on; printf \"\\033[\$(tput lines);0fPane has been maximized, press + to restore\n\" '$current_pane'") +# session_window=${info%.*} +# new_pane=${info#*.} +# +# retry=1000 +# while [ x"$(tmux list-panes -t "$session_window" -F '#{session_name}:#{window_index}.#{pane_id} #{pane_dead}' 2>/dev/null)" != x"$info 1" ] && [ "$retry" -ne 0 ]; do +# sleep 0.1 +# retry=$((retry - 1)) +# done +# if [ "$retry" -eq 0 ]; then +# tmux display 'Unable to maximize pane' +# fi +# +# tmux setw -t "$session_window" remain-on-exit off \; swap-pane -s "$current_pane" -t "$new_pane" +# else +# $restore || tmux kill-pane +# fi +# } +# +# _toggle_mouse() { +# old=$(tmux show -gv mouse) +# new="" +# +# if [ "$old" = "on" ]; then +# new="off" +# else +# new="on" +# fi +# +# tmux set -g mouse $new +# } +# +# _battery_info() { +# count=0 +# charge=0 +# case "$_uname_s" in +# *Darwin*) +# while IFS= read -r line; do +# [ -z "$line" ] && continue +# discharging=$(printf '%s' "$line" | grep -qi "discharging" && echo "true" || echo "false") +# percentage=$(printf '%s' "$line" | grep -E -o '[0-9]+%') +# charge=$(awk -v charge="$charge" -v percentage="${percentage%%%}" 'BEGIN { print charge + percentage / 100 }') +# count=$((count + 1)) +# done << EOF +# $(pmset -g batt | grep 'InternalBattery') +# EOF +# ;; +# *Linux*) +# while IFS= read -r batpath; do +# [ -z "$batpath" ] && continue +# grep -i -q device "$batpath/scope" 2> /dev/null && continue +# +# discharging=$(grep -qi "discharging" "$batpath/status" && echo "true" || echo "false") +# bat_capacity="$batpath/capacity" +# if [ -r "$bat_capacity" ]; then +# charge=$(awk -v charge="$charge" -v capacity="$(cat "$bat_capacity")" 'BEGIN { print charge + (capacity > 100 ? 100 : capacity) / 100 }') +# else +# bat_energy_full="$batpath/energy_full" +# bat_energy_now="$batpath/energy_now" +# if [ -r "$bat_energy_full" ] && [ -r "$bat_energy_now" ]; then +# charge=$(awk -v charge="$charge" -v energy_now="$(cat "$bat_energy_now")" -v energy_full="$(cat "$bat_energy_full")" 'BEGIN { print charge + energy_now / energy_full }') +# fi +# fi +# count=$((count + 1)) +# done << EOF +# $(find /sys/class/power_supply -maxdepth 1 -iname '*bat*') +# EOF +# ;; +# *CYGWIN*|*MSYS*|*MINGW*) +# while IFS= read -r line; do +# [ -z "$line" ] && continue +# discharging=$(printf '%s' "$line" | awk '{ s = ($1 == 1) ? "true" : "false"; print s }') +# charge=$(printf '%s' "$line" | awk -v charge="$charge" '{ print charge + $2 / 100 }') +# count=$((count + 1)) +# done << EOF +# $(wmic path Win32_Battery get BatteryStatus, EstimatedChargeRemaining 2> /dev/null | tr -d '\r' | tail -n +2 || true) +# EOF +# ;; +# *OpenBSD*) +# for batid in 0 1 2; do +# sysctl -n "hw.sensors.acpibat$batid.raw0" 2>&1 | grep -q 'not found' && continue +# discharging=$(sysctl -n "hw.sensors.acpibat$batid.raw0" | grep -q 1 && echo "true" || echo "false") +# if sysctl -n "hw.sensors.acpibat$batid" | grep -q amphour; then +# charge=$(awk -v charge="$charge" -v remaining="$(sysctl -n hw.sensors.acpibat$batid.amphour3 | cut -d' ' -f1)" -v full="$(sysctl -n hw.sensors.acpibat$batid.amphour0 | cut -d' ' -f1)" 'BEGIN { print charge + remaining / full }') +# else +# charge=$(awk -v charge="$charge" -v remaining="$(sysctl -n hw.sensors.acpibat$batid.watthour3 | cut -d' ' -f1)" -v full="$(sysctl -n hw.sensors.acpibat$batid.watthour0 | cut -d' ' -f1)" 'BEGIN { print charge + remaining / full }') +# fi +# count=$((count + 1)) +# done +# ;; +# esac +# [ "$count" -ne 0 ] && charge=$(awk -v charge="$charge" -v count="$count" 'BEGIN { print charge / count }') || true +# } +# +# _battery_status() { +# _battery_info +# if [ "$charge" = 0 ]; then +# tmux set -ug '@battery_status' +# return +# fi +# +# battery_status_charging=$1 +# battery_status_discharging=$2 +# if [ x"$discharging" = x"true" ]; then +# battery_status="$battery_status_discharging" +# else +# battery_status="$battery_status_charging" +# fi +# +# tmux set -g '@battery_status' "$battery_status" +# } +# +# _battery_bar() { +# _battery_info +# if [ "$charge" = 0 ]; then +# tmux set -ug '@battery_bar' \;\ +# set -ug '@battery_hbar' \;\ +# set -ug '@battery_vbar' \;\ +# set -ug '@battery_percentage' +# return +# fi +# +# battery_bar_symbol_full=$1 +# battery_bar_symbol_empty=$2 +# battery_bar_length=$3 +# battery_bar_palette=$4 +# battery_hbar_palette=$5 +# battery_vbar_palette=$6 +# +# if [ x"$battery_bar_length" = x"auto" ]; then +# columns=$(tmux -q display -p '#{client_width}' 2> /dev/null || echo 80) +# if [ "$columns" -ge 80 ]; then +# battery_bar_length=10 +# else +# battery_bar_length=5 +# fi +# fi +# +# if echo "$battery_bar_palette" | grep -q -E '^heat|gradient(,[#a-z0-9]{7,9})?$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_bar_palette; unset IFS; set +f; } +# palette_style=$1 +# battery_bg=${2:-none} +# [ x"$palette_style" = x"gradient" ] && \ +# palette="196 202 208 214 220 226 190 154 118 82 46" +# [ x"$palette_style" = x"heat" ] && \ +# palette="243 245 247 144 143 142 184 214 208 202 196" +# +# palette=$(echo "$palette" | awk -v n="$battery_bar_length" '{ for (i = 0; i < n; ++i) printf $(1 + (i * NF / n))" " }') +# eval set -- "$palette" +# +# full=$(awk "BEGIN { printf \"%.0f\", ($charge) * $battery_bar_length }") +# battery_bar="#[bg=$battery_bg]" +# # shellcheck disable=SC2046 +# [ "$full" -gt 0 ] && \ +# battery_bar="$battery_bar$(printf "#[fg=colour%s]$battery_bar_symbol_full" $(echo "$palette" | cut -d' ' -f1-"$full"))" +# # shellcheck disable=SC2046 +# empty=$((battery_bar_length - full)) +# # shellcheck disable=SC2046 +# [ "$empty" -gt 0 ] && \ +# battery_bar="$battery_bar$(printf "#[fg=colour%s]$battery_bar_symbol_empty" $(echo "$palette" | cut -d' ' -f$((full + 1))-$((full + empty))))" +# eval battery_bar="$battery_bar#[fg=colour\${$((full == 0 ? 1 : full))}]" +# elif echo "$battery_bar_palette" | grep -q -E '^(([#a-z0-9]{7,9}|none),?){3}$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_bar_palette; unset IFS; set +f; } +# battery_full_fg=$1 +# battery_empty_fg=$2 +# battery_bg=$3 +# +# full=$(awk "BEGIN { printf \"%.0f\", ($charge) * $battery_bar_length }") +# [ x"$battery_bg" != x"none" ] && \ +# battery_bar="#[bg=$battery_bg]" +# #shellcheck disable=SC2046 +# [ "$full" -gt 0 ] && \ +# battery_bar="$battery_bar#[fg=$battery_full_fg]$(printf "%0.s$battery_bar_symbol_full" $(seq 1 "$full"))" +# empty=$((battery_bar_length - full)) +# #shellcheck disable=SC2046 +# [ "$empty" -gt 0 ] && \ +# battery_bar="$battery_bar#[fg=$battery_empty_fg]$(printf "%0.s$battery_bar_symbol_empty" $(seq 1 "$empty"))" && \ +# battery_bar="$battery_bar#[fg=$battery_empty_fg]" +# fi +# +# if echo "$battery_hbar_palette" | grep -q -E '^heat|gradient(,[#a-z0-9]{7,9})?$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_hbar_palette; unset IFS; set +f; } +# palette_style=$1 +# [ x"$palette_style" = x"gradient" ] && \ +# palette="196 202 208 214 220 226 190 154 118 82 46" +# [ x"$palette_style" = x"heat" ] && \ +# palette="233 234 235 237 239 241 243 245 247 144 143 142 184 214 208 202 196" +# +# palette=$(echo "$palette" | awk -v n="$battery_bar_length" '{ for (i = 0; i < n; ++i) printf $(1 + (i * NF / n))" " }') +# eval set -- "$palette" +# +# full=$(awk "BEGIN { printf \"%.0f\", ($charge) * $battery_bar_length }") +# eval battery_hbar_fg="colour\${$((full == 0 ? 1 : full))}" +# elif echo "$battery_hbar_palette" | grep -q -E '^([#a-z0-9]{7,9},?){3}$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_hbar_palette; unset IFS; set +f; } +# +# # shellcheck disable=SC2046 +# eval $(awk "BEGIN { printf \"battery_hbar_fg=$%d\", (($charge) - 0.001) * $# + 1 }") +# fi +# +# eval set -- "▏ ▎ ▍ ▌ ▋ ▊ ▉ █" +# # shellcheck disable=SC2046 +# eval $(awk "BEGIN { printf \"battery_hbar_symbol=$%d\", ($charge) * ($# - 1) + 1 }") +# battery_hbar="#[fg=${battery_hbar_fg?}]${battery_hbar_symbol?}" +# +# if echo "$battery_vbar_palette" | grep -q -E '^heat|gradient(,[#a-z0-9]{7,9})?$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_vbar_palette; unset IFS; set +f; } +# palette_style=$1 +# [ x"$palette_style" = x"gradient" ] && \ +# palette="196 202 208 214 220 226 190 154 118 82 46" +# [ x"$palette_style" = x"heat" ] && \ +# palette="233 234 235 237 239 241 243 245 247 144 143 142 184 214 208 202 196" +# +# palette=$(echo "$palette" | awk -v n="$battery_bar_length" '{ for (i = 0; i < n; ++i) printf $(1 + (i * NF / n))" " }') +# eval set -- "$palette" +# +# full=$(awk "BEGIN { printf \"%.0f\", ($charge) * $battery_bar_length }") +# eval battery_vbar_fg="colour\${$((full == 0 ? 1 : full))}" +# elif echo "$battery_vbar_palette" | grep -q -E '^([#a-z0-9]{7,9},?){3}$'; then +# # shellcheck disable=SC2086 +# { set -f; IFS=,; set -- $battery_vbar_palette; unset IFS; set +f; } +# +# # shellcheck disable=SC2046 +# eval $(awk "BEGIN { printf \"battery_vbar_fg=$%d\", (($charge) - 0.001) * $# + 1 }") +# fi +# +# eval set -- "▁ ▂ ▃ ▄ ▅ ▆ ▇ █" +# # shellcheck disable=SC2046 +# eval $(awk "BEGIN { printf \"battery_vbar_symbol=$%d\", ($charge) * ($# - 1) + 1 }") +# battery_vbar="#[fg=${battery_vbar_fg?}]${battery_vbar_symbol?}" +# +# battery_percentage="$(awk "BEGIN { printf \"%.0f%%\", ($charge) * 100 }")" +# +# tmux set -g '@battery_status' "$battery_status" \;\ +# set -g '@battery_bar' "$battery_bar" \;\ +# set -g '@battery_hbar' "$battery_hbar" \;\ +# set -g '@battery_vbar' "$battery_vbar" \;\ +# set -g '@battery_percentage' "$battery_percentage" +# } +# +# _tty_info() { +# tty="${1##/dev/}" +# case "$_uname_s" in +# *CYGWIN*) +# ps -al | tail -n +2 | awk -v tty="$tty" ' +# ((/ssh/ && !/-W/) || !/ssh/) && $5 == tty { +# user[$1] = $6; parent[$1] = $2; child[$2] = $1 +# } +# END { +# for (i in parent) +# { +# j = i +# while (parent[j]) +# j = parent[j] +# +# if (!(i in child) && j != 1) +# { +# file = "/proc/" i "/cmdline"; getline command < file; close(file) +# gsub(/\0/, " ", command) +# "id -un " user[i] | getline username +# print i":"username":"command +# exit +# } +# } +# } +# ' +# ;; +# *) +# ps -t "$tty" -o user=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -o pid= -o ppid= -o command= | awk ' +# NR > 1 && ((/ssh/ && !/-W/) || !/ssh/) { +# user[$2] = $1; parent[$2] = $3; child[$3] = $2; pid=$2; $1 = $2 = $3 = ""; command[pid] = substr($0,4) +# } +# END { +# for (i in parent) +# { +# j = i +# while (parent[j]) +# j = parent[j] +# +# if (!(i in child) && j != 1) +# { +# print i":"user[i]":"command[i] +# exit +# } +# } +# } +# ' +# ;; +# esac +# } +# +# _ssh_or_mosh_args() { +# case "$1" in +# *ssh*) +# args=$(printf '%s' "$1" | perl -n -e 'print if s/(.*?)\bssh\b\s+(.*)/\2/') +# ;; +# *mosh-client*) +# args=$(printf '%s' "$1" | sed -E -e 's/.*mosh-client -# (.*)\|.*$/\1/' -e 's/-[^ ]*//g' -e 's/\d:\d//g') +# ;; +# esac +# +# printf '%s' "$args" +# } +# +# _username() { +# tty=${1:-$(tmux display -p '#{pane_tty}')} +# ssh_only=$2 +# +# tty_info=$(_tty_info "$tty") +# command=${tty_info#*:} +# command=${command#*:} +# +# ssh_or_mosh_args=$(_ssh_or_mosh_args "$command") +# if [ -n "$ssh_or_mosh_args" ]; then +# # shellcheck disable=SC2086 +# username=$(ssh -G $ssh_or_mosh_args 2>/dev/null | awk '/^user / { print $2; exit }') +# # shellcheck disable=SC2086 +# [ -z "$username" ] && username=$(ssh -T -o ControlPath=none -o ProxyCommand="sh -c 'echo %%username%% %r >&2'" $ssh_or_mosh_args 2>&1 | awk '/^%username% / { print $2; exit }') +# else +# if ! _is_enabled "$ssh_only"; then +# username=${tty_info#*:} +# username=${username%%:*} +# fi +# fi +# +# printf '%s\n' "$username" +# } +# +# _hostname() { +# tty=${1:-$(tmux display -p '#{pane_tty}')} +# ssh_only=$2 +# full=$3 +# h_or_H=$4 +# +# tty_info=$(_tty_info "$tty") +# command=${tty_info#*:} +# command=${command#*:} +# +# ssh_or_mosh_args=$(_ssh_or_mosh_args "$command") +# if [ -n "$ssh_or_mosh_args" ]; then +# # shellcheck disable=SC2086 +# hostname=$(ssh -G $ssh_or_mosh_args 2>/dev/null | awk '/^hostname / { print $2; exit }') +# # shellcheck disable=SC2086 +# [ -z "$hostname" ] && hostname=$(ssh -T -o ControlPath=none -o ProxyCommand="sh -c 'echo %%hostname%% %h >&2'" $ssh_or_mosh_args 2>&1 | awk '/^%hostname% / { print $2; exit }') +# +# if ! _is_enabled "$full"; then +# case "$hostname" in +# *[a-z-].*) +# hostname=${hostname%%.*} +# ;; +# 127.0.0.1) +# hostname="localhost" +# ;; +# esac +# fi +# else +# if ! _is_enabled "$ssh_only"; then +# hostname="$h_or_H" +# fi +# fi +# +# printf '%s\n' "$hostname" +# } +# +# _root() { +# tty=${1:-$(tmux display -p '#{pane_tty}')} +# root=$2 +# +# username=$(_username "$tty" false) +# +# [ x"$username" = x"root" ] && echo "$root" +# } +# +# _uptime() { +# case "$_uname_s" in +# *Darwin*|*FreeBSD*) +# boot=$(sysctl -q -n kern.boottime | awk -F'[ ,:]+' '{ print $4 }') +# now=$(date +%s) +# ;; +# *Linux*|*CYGWIN*|*MSYS*|*MINGW*) +# boot=0 +# now=$(cut -d' ' -f1 < /proc/uptime) +# ;; +# *OpenBSD*) +# boot=$(sysctl -n kern.boottime) +# now=$(date +%s) +# esac +# # shellcheck disable=SC1004 +# awk -v boot="$boot" -v now="$now" ' +# BEGIN { +# uptime = now - boot +# y = int(uptime / 31536000) +# dy = int(uptime / 86400) % 365 +# d = int(uptime / 86400) +# h = int(uptime / 3600) % 24 +# m = int(uptime / 60) % 60 +# s = int(uptime) % 60 +# +# system("tmux set -g @uptime_y " y + 0 " \\; " \ +# "set -g @uptime_dy " dy + 0 " \\; " \ +# "set -g @uptime_d " d + 0 " \\; " \ +# "set -g @uptime_h " h + 0 " \\; " \ +# "set -g @uptime_m " m + 0 " \\; " \ +# "set -g @uptime_s " s + 0) +# }' +# } +# +# _loadavg() { +# case "$_uname_s" in +# *Darwin*|*FreeBSD*) +# tmux set -g @loadavg "$(sysctl -q -n vm.loadavg | cut -d' ' -f2)" +# ;; +# *Linux*|*CYGWIN*) +# tmux set -g @loadavg "$(cut -d' ' -f1 < /proc/loadavg)" +# ;; +# *OpenBSD*) +# tmux set -g @loadavg "$(sysctl -q -n vm.loadavg | cut -d' ' -f1)" +# ;; +# esac +# } +# +# _split_window_ssh() { +# tty=${1:-$(tmux display -p '#{pane_tty}')} +# shift +# +# tty_info=$(_tty_info "$tty") +# command=${tty_info#*:} +# command=${command#*:} +# +# case "$command" in +# *mosh-client*) +# # shellcheck disable=SC2046 +# tmux split-window "$@" mosh $(echo "$command" | sed -E -e 's/.*mosh-client -# (.*)\|.*$/\1/') +# ;; +# *ssh*) +# # shellcheck disable=SC2046 +# tmux split-window "$@" $(echo "$command" | sed -e 's/;/\\;/g') +# ;; +# *) +# tmux split-window "$@" +# esac +# } +# +# _split_window() { +# _split_window_ssh "$@" +# } +# +# _apply_overrides() { +# tmux_conf_theme_24b_colour=${tmux_conf_theme_24b_colour:-false} +# tmux_conf_24b_colour=${tmux_conf_24b_colour:-$tmux_conf_theme_24b_colour} +# if _is_enabled "$tmux_conf_24b_colour"; then +# case "$TERM" in +# screen-*|tmux-*) +# ;; +# *) +# tmux set-option -ga terminal-overrides ",*256col*:Tc" +# ;; +# esac +# fi +# } +# +# _apply_bindings() { +# cfg=$(mktemp) && trap 'rm -f $cfg*' EXIT +# +# tmux list-keys | grep -vF 'tmux.conf.local' | grep -E 'new-window|split(-|_)window|new-session|copy-selection|copy-pipe' > "$cfg" +# +# # tmux 3.0 doesn't include 02254d1e5c881be95fd2fc37b4c4209640b6b266 and the +# # output of list-keys can be truncated +# perl -p -i -e "s/'#\{\?window_zoomed_flag,Unzoom,Zoom\}' 'z' \{resize-pane -$/'#{?window_zoomed_flag,Unzoom,Zoom}' 'z' {resize-pane -Z}\"/g" "$cfg" +# +# perl -p -i -e " +# s/\bnew-window\b([^;}\n]*?)(?:\s+-c\s+((?:\\\\\")?|\"?|'?)#\{pane_current_path\}\2)/new-window\1/g +# ; +# s/\brun-shell\b\s+(\"|')cut\s+-c3-\s+~\/\.tmux\.conf\s+\|\s+sh\s+-s\s+_split_window\s+#\{pane_tty\}([^\n\1]*)(\s+-c\s+((?:\\\\\")?|\"?|'?)#\{pane_current_path\}\4)([^\n\1]*)\1/run-shell \1cut -c3- ~\/.tmux.conf | sh -s _split_window #{pane_tty}\2\5\1/g +# ; +# s/\brun-shell\b(\s+((?:\\\\\")?|\"?|'?)cut\s+-c3-\s+~\/\.tmux\.conf\s+\|\s+sh\s+-s\s+_split_window\s+((?:\\\\\")?|\"?|'?)#\{pane_tty\}\3)(.*?)\2/split-window\4/g +# ; +# s/\bsplit-window\b([^;}\n]*?)(?:\s+-c\s+((?:\\\\\")?|\"?|'?)#\{pane_current_path\}\2)/split-window\1/g" \ +# "$cfg" +# +# tmux_conf_new_window_retain_current_path=${tmux_conf_new_window_retain_current_path:-false} +# if _is_enabled "$tmux_conf_new_window_retain_current_path"; then +# perl -p -i -e " +# s/\bnew-window\b(?!\s+-)/{$&}/g if /\bdisplay-menu\b/ +# ; +# s/\bnew-window\b/new-window -c '#\{pane_current_path\}'/g" \ +# "$cfg" +# fi +# +# perl -p -i -e " +# s/\bsplit-window\b((?:(?:[ \t]+-[bdfhIvP])|(?:[ \t]+-[celtF][ \t]+(?!\bssh\b)[^\s]+))*)?(?:\s+(\bssh\b))((?:(?:[ \t]+-[bdfhIvP])|(?:[ \t]+-[celtF][ \t]+(?!\bssh\b)[^\s]+))*)?/run-shell 'cut -c3- ~\/\.tmux\.conf | sh -s _split_window_ssh #\{pane_tty\}\1'/g if /\bsplit-window\b((?:(?:[ \t]+-[bdfhIvP])|(?:[ \t]+-[celtF][ \t]+(?!ssh)[^\s]+))*)?(?:\s+(ssh))((?:(?:[ \t]+-[bdfhIvP])|(?:[ \t]+-[celtF][ \t]+(?!ssh)[^\s]+))*)?/"\ +# "$cfg" +# +# tmux_conf_new_pane_retain_current_path=${tmux_conf_new_pane_retain_current_path:-true} +# tmux_conf_new_pane_reconnect_ssh=${tmux_conf_new_pane_reconnect_ssh:-false} +# if _is_enabled "$tmux_conf_new_pane_reconnect_ssh"; then +# perl -p -i -e "s/\bsplit-window\b([^;}\n\"]*)/run-shell 'cut -c3- ~\/\.tmux\.conf | sh -s _split_window #\{pane_tty\}\1'/g" "$cfg" +# fi +# +# if _is_enabled "$tmux_conf_new_pane_retain_current_path"; then +# perl -p -i -e " +# s/\bsplit-window\b(?!\s+-)/{$&}/g if /\bdisplay-menu\b/ +# ; +# s/\bsplit-window\b/split-window -c '#{pane_current_path}'\1/g +# ; +# s/\brun-shell\b\s+'cut\s+-c3-\s+~\/\.tmux\.conf\s+\|\s+sh\s+-s\s+_split_window(_ssh)?\s+#\{pane_tty\}([^}\n']*)'/run-shell 'cut -c3- ~\/.tmux.conf | sh -s _split_window\1 #\{pane_tty\} -c \\\\\"#\{pane_current_path\}\\\\\"\2'/g if /\bdisplay-menu\b/ +# ; +# s/\brun-shell\b\s+'cut\s+-c3-\s+~\/\.tmux\.conf\s+\|\s+sh\s+-s\s+_split_window(_ssh)?\s+#\{pane_tty\}([^}\n']*)'/run-shell 'cut -c3- ~\/.tmux.conf | sh -s _split_window\1 #\{pane_tty\} -c \"#\{pane_current_path\}\"\2'/g" \ +# "$cfg" +# fi +# +# tmux_conf_new_session_prompt=${tmux_conf_new_session_prompt:-false} +# if _is_enabled "$tmux_conf_new_session_prompt"; then +# perl -p -i \ +# -e "s/(? /dev/null 2>&1 && command='xsel -i -b' +# ! command -v xsel > /dev/null 2>&1 && command -v xclip > /dev/null 2>&1 && command='xclip -i -selection clipboard > \/dev\/null 2>\&1' +# command -v pbcopy > /dev/null 2>&1 && command='pbcopy' +# command -v reattach-to-user-namespace > /dev/null 2>&1 && command='reattach-to-user-namespace pbcopy' +# command -v clip.exe > /dev/null 2>&1 && command='clip\.exe' +# [ -c /dev/clipboard ] && command='cat > \/dev\/clipboard' +# +# if [ -n "$command" ]; then +# if _is_enabled "$tmux_conf_copy_to_os_clipboard"; then +# perl -p -i -e "s/(?!.*?$command)\bcopy-(?:selection|pipe)(-and-cancel)?\b/copy-pipe\1 '$command'/g" "$cfg" +# else +# if [ $_tmux_version -ge 320 ]; then +# perl -p -i -e "s/\bcopy-pipe(-and-cancel)?\b\s+(\"|')?$command\2?/copy-pipe\1/g" "$cfg" +# else +# perl -p -i -e "s/\bcopy-pipe(-and-cancel)?\b\s+(\"|')?$command\2?/copy-selection\1/g" "$cfg" +# fi +# fi +# fi +# +# # until tmux >= 3.0, output of tmux list-keys can't be consumed back by tmux source-file without applying some escapings +# awk < "$cfg" \ +# '{i = $2 == "-T" ? 4 : 5; gsub(/^[;]$/, "\\\\&", $i); gsub(/^[$"#~]$/, "'"'"'&'"'"'", $i); gsub(/^['"'"']$/, "\"&\"", $i); print}' > "$cfg.in" +# +# # ignore bindings with errors +# if ! tmux source-file "$cfg.in"; then +# verbose_flag=$(tmux source-file -v /dev/null 2> /dev/null && printf -- '-v' || true) +# while ! out=$(tmux source-file "$verbose_flag" "$cfg.in"); do +# line=$(printf "%s" "$out" | tail -1 | cut -d':' -f2) +# perl -n -i -e "if ($. != $line) { print }" "$cfg.in" +# done +# fi +# } +# +# _apply_theme() { +# +# # -- default theme ----------------------------------------------------- +# +# tmux_conf_theme_colour_1=${tmux_conf_theme_colour_1:-#080808} # dark gray +# tmux_conf_theme_colour_2=${tmux_conf_theme_colour_2:-#303030} # gray +# tmux_conf_theme_colour_3=${tmux_conf_theme_colour_3:-#8a8a8a} # light gray +# tmux_conf_theme_colour_4=${tmux_conf_theme_colour_4:-#00afff} # light blue +# tmux_conf_theme_colour_5=${tmux_conf_theme_colour_5:-#ffff00} # yellow +# tmux_conf_theme_colour_6=${tmux_conf_theme_colour_6:-#080808} # dark gray +# tmux_conf_theme_colour_7=${tmux_conf_theme_colour_7:-#e4e4e4} # white +# tmux_conf_theme_colour_8=${tmux_conf_theme_colour_8:-#080808} # dark gray +# tmux_conf_theme_colour_9=${tmux_conf_theme_colour_9:-#ffff00} # yellow +# tmux_conf_theme_colour_10=${tmux_conf_theme_colour_10:-#ff00af} # pink +# tmux_conf_theme_colour_11=${tmux_conf_theme_colour_11:-#5fff00} # green +# tmux_conf_theme_colour_12=${tmux_conf_theme_colour_12:-#8a8a8a} # light gray +# tmux_conf_theme_colour_13=${tmux_conf_theme_colour_13:-#e4e4e4} # white +# tmux_conf_theme_colour_14=${tmux_conf_theme_colour_14:-#080808} # dark gray +# tmux_conf_theme_colour_15=${tmux_conf_theme_colour_15:-#080808} # dark gray +# tmux_conf_theme_colour_16=${tmux_conf_theme_colour_16:-#d70000} # red +# tmux_conf_theme_colour_17=${tmux_conf_theme_colour_17:-#e4e4e4} # white +# +# # -- panes ------------------------------------------------------------- +# +# tmux_conf_theme_window_fg=${tmux_conf_theme_window_fg:-default} +# tmux_conf_theme_window_bg=${tmux_conf_theme_window_bg:-default} +# tmux_conf_theme_highlight_focused_pane=${tmux_conf_theme_highlight_focused_pane:-false} +# tmux_conf_theme_focused_pane_fg=${tmux_conf_theme_focused_pane_fg:-default} +# tmux_conf_theme_focused_pane_bg=${tmux_conf_theme_focused_pane_bg:-$tmux_conf_theme_colour_2} +# +# window_style="fg=$tmux_conf_theme_window_fg,bg=$tmux_conf_theme_window_bg" +# if _is_enabled "$tmux_conf_theme_highlight_focused_pane"; then +# window_active_style="fg=$tmux_conf_theme_focused_pane_fg,bg=$tmux_conf_theme_focused_pane_bg" +# else +# window_active_style="default" +# fi +# +# tmux_conf_theme_pane_border_style=${tmux_conf_theme_pane_border_style:-thin} +# tmux_conf_theme_pane_border=${tmux_conf_theme_pane_border:-$tmux_conf_theme_colour_2} +# tmux_conf_theme_pane_active_border=${tmux_conf_theme_pane_active_border:-$tmux_conf_theme_colour_4} +# tmux_conf_theme_pane_border_fg=${tmux_conf_theme_pane_border_fg:-$tmux_conf_theme_pane_border} +# tmux_conf_theme_pane_active_border_fg=${tmux_conf_theme_pane_active_border_fg:-$tmux_conf_theme_pane_active_border} +# case "$tmux_conf_theme_pane_border_style" in +# fat) +# tmux_conf_theme_pane_border_bg=${tmux_conf_theme_pane_border_bg:-$tmux_conf_theme_pane_border_fg} +# tmux_conf_theme_pane_active_border_bg=${tmux_conf_theme_pane_active_border_bg:-$tmux_conf_theme_pane_active_border_fg} +# ;; +# thin|*) +# tmux_conf_theme_pane_border_bg=${tmux_conf_theme_pane_border_bg:-default} +# tmux_conf_theme_pane_active_border_bg=${tmux_conf_theme_pane_active_border_bg:-default} +# ;; +# esac +# +# tmux_conf_theme_pane_indicator=${tmux_conf_theme_pane_indicator:-$tmux_conf_theme_colour_4} +# tmux_conf_theme_pane_active_indicator=${tmux_conf_theme_pane_active_indicator:-$tmux_conf_theme_colour_4} +# +# # -- status line ------------------------------------------------------- +# +# tmux_conf_theme_left_separator_main=$(_decode_unicode_escapes "${tmux_conf_theme_left_separator_main-}") +# tmux_conf_theme_left_separator_sub=$(_decode_unicode_escapes "${tmux_conf_theme_left_separator_sub-|}") +# tmux_conf_theme_right_separator_main=$(_decode_unicode_escapes "${tmux_conf_theme_right_separator_main-}") +# tmux_conf_theme_right_separator_sub=$(_decode_unicode_escapes "${tmux_conf_theme_right_separator_sub-|}") +# +# tmux_conf_theme_message_fg=${tmux_conf_theme_message_fg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_message_bg=${tmux_conf_theme_message_bg:-$tmux_conf_theme_colour_5} +# tmux_conf_theme_message_attr=${tmux_conf_theme_message_attr:-bold} +# +# tmux_conf_theme_message_command_fg=${tmux_conf_theme_message_command_fg:-$tmux_conf_theme_colour_5} +# tmux_conf_theme_message_command_bg=${tmux_conf_theme_message_command_bg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_message_command_attr=${tmux_conf_theme_message_command_attr:-bold} +# +# tmux_conf_theme_mode_fg=${tmux_conf_theme_mode_fg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_mode_bg=${tmux_conf_theme_mode_bg:-$tmux_conf_theme_colour_5} +# tmux_conf_theme_mode_attr=${tmux_conf_theme_mode_attr:-bold} +# +# tmux_conf_theme_status_fg=${tmux_conf_theme_status_fg:-$tmux_conf_theme_colour_3} +# tmux_conf_theme_status_bg=${tmux_conf_theme_status_bg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_status_attr=${tmux_conf_theme_status_attr:-none} +# +# tmux_conf_theme_terminal_title=${tmux_conf_theme_terminal_title:-#h ❐ #S ● #I #W} +# +# tmux_conf_theme_terminal_title=$(echo "$tmux_conf_theme_terminal_title" | sed \ +# -e 's%#{circled_window_index}%#(cut -c3- ~/.tmux.conf | sh -s _circled #I)%g' \ +# -e 's%#{circled_session_name}%#(cut -c3- ~/.tmux.conf | sh -s _circled #S)%g' \ +# -e 's%#{username}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} false #D)%g' \ +# -e 's%#{hostname}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false false #h #D)%g' \ +# -e 's%#{hostname_full}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false true #H #D)%g' \ +# -e 's%#{username_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} true #D)%g' \ +# -e 's%#{hostname_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true false #h #D)%g' \ +# -e 's%#{hostname_full_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true true #H #D)%g') +# +# tmux_conf_theme_window_status_fg=${tmux_conf_theme_window_status_fg:-$tmux_conf_theme_colour_3} +# tmux_conf_theme_window_status_bg=${tmux_conf_theme_window_status_bg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_window_status_attr=${tmux_conf_theme_window_status_attr:-none} +# tmux_conf_theme_window_status_format=${tmux_conf_theme_window_status_format:-#I #W} +# +# tmux_conf_theme_window_status_current_fg=${tmux_conf_theme_window_status_current_fg:-$tmux_conf_theme_colour_1} +# tmux_conf_theme_window_status_current_bg=${tmux_conf_theme_window_status_current_bg:-$tmux_conf_theme_colour_4} +# tmux_conf_theme_window_status_current_attr=${tmux_conf_theme_window_status_current_attr:-bold} +# tmux_conf_theme_window_status_current_format=${tmux_conf_theme_window_status_current_format:-#I #W} +# +# tmux_conf_theme_window_status_activity_fg=${tmux_conf_theme_window_status_activity_fg:-default} +# tmux_conf_theme_window_status_activity_bg=${tmux_conf_theme_window_status_activity_bg:-default} +# tmux_conf_theme_window_status_activity_attr=${tmux_conf_theme_window_status_activity_attr:-underscore} +# +# tmux_conf_theme_window_status_bell_fg=${tmux_conf_theme_window_status_bell_fg:-$tmux_conf_theme_colour_5} +# tmux_conf_theme_window_status_bell_bg=${tmux_conf_theme_window_status_bell_bg:-default} +# tmux_conf_theme_window_status_bell_attr=${tmux_conf_theme_window_status_bell_attr:-blink,bold} +# +# tmux_conf_theme_window_status_last_fg=${tmux_conf_theme_window_status_last_fg:-$tmux_conf_theme_colour_4} +# tmux_conf_theme_window_status_last_bg=${tmux_conf_theme_window_status_last_bg:-default} +# tmux_conf_theme_window_status_last_attr=${tmux_conf_theme_window_status_last_attr:-none} +# +# if [ x"$tmux_conf_theme_window_status_bg" = x"$tmux_conf_theme_status_bg" ] || [ x"$tmux_conf_theme_window_status_bg" = x"default" ]; then +# spacer='' +# spacer_current=' ' +# else +# spacer=' ' +# spacer_current=' ' +# fi +# if [ x"$tmux_conf_theme_window_status_last_bg" = x"$tmux_conf_theme_status_bg" ] || [ x"$tmux_conf_theme_window_status_last_bg" = x"default" ] ; then +# spacer_last='' +# else +# spacer_last=' ' +# fi +# if [ x"$tmux_conf_theme_window_status_activity_bg" = x"$tmux_conf_theme_status_bg" ] || [ x"$tmux_conf_theme_window_status_activity_bg" = x"default" ] ; then +# spacer_activity='' +# spacer_last_activity="$spacer_last" +# else +# spacer_activity=' ' +# spacer_last_activity=' ' +# fi +# if [ x"$tmux_conf_theme_window_status_bell_bg" = x"$tmux_conf_theme_status_bg" ] || [ x"$tmux_conf_theme_window_status_bell_bg" = x"default" ] ; then +# spacer_bell='' +# spacer_last_bell="$spacer_last" +# spacer_activity_bell="$spacer_activity" +# spacer_last_activity_bell="$spacer_last_activity" +# else +# spacer_bell=' ' +# spacer_last_bell=' ' +# spacer_activity_bell=' ' +# spacer_last_activity_bell=' ' +# fi +# spacer="#{?window_last_flag,#{?window_activity_flag,#{?window_bell_flag,$spacer_last_activity_bell,$spacer_last_activity},#{?window_bell_flag,$spacer_last_bell,$spacer_last}},#{?window_activity_flag,#{?window_bell_flag,$spacer_activity_bell,$spacer_activity},#{?window_bell_flag,$spacer_bell,$spacer}}}" +# if [ x"$(tmux show -g -v status-justify)" = x"right" ]; then +# if [ -z "$tmux_conf_theme_right_separator_main" ]; then +# window_status_separator=' ' +# else +# window_status_separator='' +# fi +# tmux_conf_theme_window_status_format="#[fg=$tmux_conf_theme_window_status_bg,bg=$tmux_conf_theme_status_bg,none]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}$tmux_conf_theme_right_separator_main#[fg=$tmux_conf_theme_window_status_fg,bg=$tmux_conf_theme_window_status_bg,$tmux_conf_theme_window_status_attr]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}$spacer$(printf "$tmux_conf_theme_window_status_last_attr" | perl -n -e 'print "#{?window_last_flag,#[none],}" if !/default/ ; s/([a-z]+),?/#{?window_last_flag,#[\1],}/g; print if !/default/')$(printf "$tmux_conf_theme_window_status_activity_attr" | perl -n -e 'print "#{?window_activity_flag?,#[none],}" if !/default/ ; s/([a-z]+),?/#{?window_activity_flag,#[\1],}/g; print if !/default/')$(printf "$tmux_conf_theme_window_status_bell_attr" | perl -n -e 'print "#{?window_bell_flag,#[none],}" if !/default/ ; s/([a-z]+),?/#{?window_bell_flag,#[\1],}/g; print if !/default/')$tmux_conf_theme_window_status_format#[none]$spacer#[fg=$tmux_conf_theme_status_bg,bg=$tmux_conf_theme_window_status_bg]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#[none]$tmux_conf_theme_right_separator_main" +# tmux_conf_theme_window_status_current_format="#[fg=$tmux_conf_theme_window_status_current_bg,bg=$tmux_conf_theme_status_bg,none]$tmux_conf_theme_right_separator_main#[fg=$tmux_conf_theme_window_status_current_fg,bg=$tmux_conf_theme_window_status_current_bg,$tmux_conf_theme_window_status_current_attr]$spacer_current$tmux_conf_theme_window_status_current_format$spacer_current#[fg=$tmux_conf_theme_status_bg,bg=$tmux_conf_theme_window_status_current_bg,none]$tmux_conf_theme_right_separator_main" +# else +# if [ -z "$tmux_conf_theme_left_separator_main" ]; then +# window_status_separator=' ' +# else +# window_status_separator='' +# fi +# tmux_conf_theme_window_status_format="#[fg=$tmux_conf_theme_status_bg,bg=$tmux_conf_theme_window_status_bg,none]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}$tmux_conf_theme_left_separator_main#[fg=$tmux_conf_theme_window_status_fg,bg=$tmux_conf_theme_window_status_bg,$tmux_conf_theme_window_status_attr]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_fg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[bg=$&]/; print if !/default/"),}$spacer$(printf "$tmux_conf_theme_window_status_last_attr" | perl -n -e 'print "#{?window_last_flag,#[none],}" if !/default/ ; s/([a-z]+),?/#{?window_last_flag,#[\1],}/g; print if !/default/')$(printf "$tmux_conf_theme_window_status_activity_attr" | perl -n -e 'print "#{?window_activity_flag,#[none],}" if !/default/ ; s/([a-z]+),?/#{?window_activity_flag,#[\1],}/g; print if !/default/')$(printf "$tmux_conf_theme_window_status_bell_attr" | perl -n -e 'print "#{?window_bell_flag,#[none],}" if /!default/ ; s/([a-z]+),?/#{?window_bell_flag,#[\1],}/g; print if !/default/')$tmux_conf_theme_window_status_format#[none]$spacer#[fg=$tmux_conf_theme_window_status_bg,bg=$tmux_conf_theme_status_bg]#{?window_last_flag,$(printf "$tmux_conf_theme_window_status_last_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_activity_flag,$(printf "$tmux_conf_theme_window_status_activity_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}#{?window_bell_flag,$(printf "$tmux_conf_theme_window_status_bell_bg" | perl -n -e "s/.+/#[fg=$&]/; print if !/default/"),}$tmux_conf_theme_left_separator_main" +# tmux_conf_theme_window_status_current_format="#[fg=$tmux_conf_theme_status_bg,bg=$tmux_conf_theme_window_status_current_bg,none]$tmux_conf_theme_left_separator_main#[fg=$tmux_conf_theme_window_status_current_fg,bg=$tmux_conf_theme_window_status_current_bg,$tmux_conf_theme_window_status_current_attr]$spacer_current$tmux_conf_theme_window_status_current_format$spacer_current#[fg=$tmux_conf_theme_window_status_current_bg,bg=$tmux_conf_theme_status_bg]$tmux_conf_theme_left_separator_main" +# fi +# +# tmux_conf_theme_window_status_format=$(echo "$tmux_conf_theme_window_status_format" | sed \ +# -e 's%#{circled_window_index}%#(cut -c3- ~/.tmux.conf | sh -s _circled #I)%g' \ +# -e 's%#{circled_session_name}%#(cut -c3- ~/.tmux.conf | sh -s _circled #S)%g' \ +# -e 's%#{username}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} false #D)%g' \ +# -e 's%#{hostname}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false false #h #D)%g' \ +# -e 's%#{hostname_full}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false true #H #D)%g' \ +# -e 's%#{username_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} true #D)%g' \ +# -e 's%#{hostname_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true false #h #D)%g' \ +# -e 's%#{hostname_full_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true true #H #D)%g') +# tmux_conf_theme_window_status_current_format=$(echo "$tmux_conf_theme_window_status_current_format" | sed \ +# -e 's%#{circled_window_index}%#(cut -c3- ~/.tmux.conf | sh -s _circled #I)%g' \ +# -e 's%#{circled_session_name}%#(cut -c3- ~/.tmux.conf | sh -s _circled #S)%g' \ +# -e 's%#{username}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} false #D)%g' \ +# -e 's%#{hostname}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false false #h #D)%g' \ +# -e 's%#{hostname_full}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false true #H #D)%g' \ +# -e 's%#{username_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} true #D)%g' \ +# -e 's%#{hostname_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true false #h #D)%g' \ +# -e 's%#{hostname_full_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true true #H #D)%g') +# +# # -- indicators +# +# tmux_conf_theme_pairing=${tmux_conf_theme_pairing:-⚇} # U+2687 +# tmux_conf_theme_pairing_fg=${tmux_conf_theme_pairing_fg:-none} +# tmux_conf_theme_pairing_bg=${tmux_conf_theme_pairing_bg:-none} +# tmux_conf_theme_pairing_attr=${tmux_conf_theme_pairing_attr:-none} +# +# tmux_conf_theme_prefix=${tmux_conf_theme_prefix:-⌨} # U+2328 +# tmux_conf_theme_prefix_fg=${tmux_conf_theme_prefix_fg:-none} +# tmux_conf_theme_prefix_bg=${tmux_conf_theme_prefix_bg:-none} +# tmux_conf_theme_prefix_attr=${tmux_conf_theme_prefix_attr:-none} +# +# tmux_conf_theme_mouse=${tmux_conf_theme_mouse:-↗} # U+2197 +# tmux_conf_theme_mouse_fg=${tmux_conf_theme_mouse_fg:-none} +# tmux_conf_theme_mouse_bg=${tmux_conf_theme_mouse_bg:-none} +# tmux_conf_theme_mouse_attr=${tmux_conf_theme_mouse_attr:-none} +# +# tmux_conf_theme_root=${tmux_conf_theme_root:-!} +# tmux_conf_theme_root_fg=${tmux_conf_theme_root_fg:-none} +# tmux_conf_theme_root_bg=${tmux_conf_theme_root_bg:-none} +# tmux_conf_theme_root_attr=${tmux_conf_theme_root_attr:-bold,blink} +# +# tmux_conf_theme_synchronized=${tmux_conf_theme_synchronized:-⚏} # U+268F +# tmux_conf_theme_synchronized_fg=${tmux_conf_theme_synchronized_fg:-none} +# tmux_conf_theme_synchronized_bg=${tmux_conf_theme_synchronized_bg:-none} +# tmux_conf_theme_synchronized_attr=${tmux_conf_theme_synchronized_attr:-none} +# +# # -- status-left style +# +# tmux_conf_theme_status_left=${tmux_conf_theme_status_left-' ❐ #S | ↑#{?uptime_y, #{uptime_y}y,}#{?uptime_d, #{uptime_d}d,}#{?uptime_h, #{uptime_h}h,}#{?uptime_m, #{uptime_m}m,} '} +# tmux_conf_theme_status_left_fg=${tmux_conf_theme_status_left_fg:-$tmux_conf_theme_colour_6,$tmux_conf_theme_colour_7,$tmux_conf_theme_colour_8} +# tmux_conf_theme_status_left_bg=${tmux_conf_theme_status_left_bg:-$tmux_conf_theme_colour_9,$tmux_conf_theme_colour_10,$tmux_conf_theme_colour_11} +# tmux_conf_theme_status_left_attr=${tmux_conf_theme_status_left_attr:-bold,none,none} +# +# if [ -n "$tmux_conf_theme_status_left" ]; then +# status_left=$(echo "$tmux_conf_theme_status_left" | sed \ +# -e "s/#{pairing}/#[fg=$tmux_conf_theme_pairing_fg]#[bg=$tmux_conf_theme_pairing_bg]#[$tmux_conf_theme_pairing_attr]#{?session_many_attached,$tmux_conf_theme_pairing ,}/g" \ +# -e "s/#{prefix}/#[fg=$tmux_conf_theme_prefix_fg]#[bg=$tmux_conf_theme_prefix_bg]#[$tmux_conf_theme_prefix_attr]#{?client_prefix,$tmux_conf_theme_prefix ,$(printf "$tmux_conf_theme_prefix" | sed -e 's/./ /g') }/g" \ +# -e "s/#{mouse}/#[fg=$tmux_conf_theme_mouse_fg]#[bg=$tmux_conf_theme_mouse_bg]#[$tmux_conf_theme_mouse_attr]#{?mouse,$tmux_conf_theme_mouse ,$(printf "$tmux_conf_theme_mouse" | sed -e 's/./ /g') }/g" \ +# -e "s%#{synchronized}%#[fg=$tmux_conf_theme_synchronized_fg]#[bg=$tmux_conf_theme_synchronized_bg]#[$tmux_conf_theme_synchronized_attr]#{?pane_synchronized,$tmux_conf_theme_synchronized ,}%g" \ +# -e 's%#{circled_session_name}%#(cut -c3- ~/.tmux.conf | sh -s _circled #S)%g') +# +# if [ -n "$(tmux display -p '#{version}')" ]; then +# status_left=$(echo "$status_left" | sed \ +# -e "s%#{root}%#[fg=$tmux_conf_theme_root_fg]#[bg=$tmux_conf_theme_root_bg]#[$tmux_conf_theme_root_attr]#{?#{==:#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} #D),root},$tmux_conf_theme_root,}#[inherit]%g") +# else +# status_left=$(echo "$status_left" | sed \ +# -e "s%#{root}%#[fg=$tmux_conf_theme_root_fg]#[bg=$tmux_conf_theme_root_bg]#[$tmux_conf_theme_root_attr]#(cut -c3- ~/.tmux.conf | sh -s _root #{pane_tty} $tmux_conf_theme_root #D)#[inherit]%g") +# fi +# +# status_left=$(printf '%s' "$status_left" | awk \ +# -v status_bg="$tmux_conf_theme_status_bg" \ +# -v fg_="$tmux_conf_theme_status_left_fg" \ +# -v bg_="$tmux_conf_theme_status_left_bg" \ +# -v attr_="$tmux_conf_theme_status_left_attr" \ +# -v mainsep="$tmux_conf_theme_left_separator_main" \ +# -v subsep="$tmux_conf_theme_left_separator_sub" ' +# function subsplit(s, l, i, a, r) +# { +# l = split(s, a, ",") +# for (i = 1; i <= l; ++i) +# { +# o = split(a[i], _, "(") - 1 +# c = split(a[i], _, ")") - 1 +# open += o - c +# o_ = split(a[i], _, "{") - 1 +# c_ = split(a[i], _, "}") - 1 +# open_ += o_ - c_ +# o__ = split(a[i], _, "[") - 1 +# c__ = split(a[i], _, "]") - 1 +# open__ += o__ - c__ +# +# if (i == l) +# r = sprintf("%s%s", r, a[i]) +# else if (open || open_ || open__) +# r = sprintf("%s%s,", r, a[i]) +# else +# r = sprintf("%s%s#[fg=%s,bg=%s,%s]%s", r, a[i], fg[j], bg[j], attr[j], subsep) +# } +# +# gsub(/#\[inherit\]/, sprintf("#[default]#[fg=%s,bg=%s,%s]", fg[j], bg[j], attr[j]), r) +# return r +# } +# BEGIN { +# FS = "|" +# l1 = split(fg_, fg, ",") +# l2 = split(bg_, bg, ",") +# l3 = split(attr_, attr, ",") +# l = l1 < l2 ? (l1 < l3 ? l1 : l3) : (l2 < l3 ? l2 : l3) +# } +# { +# for (i = j = 1; i <= NF; ++i) +# { +# if (open || open_ || open__) +# printf "|%s", subsplit($i) +# else +# { +# if (i > 1) +# printf "#[fg=%s,bg=%s,none]%s#[fg=%s,bg=%s,%s]%s", bg[j_], bg[j], mainsep, fg[j], bg[j], attr[j], subsplit($i) +# else +# printf "#[fg=%s,bg=%s,%s]%s", fg[j], bg[j], attr[j], subsplit($i) +# } +# +# if (!open && !open_ && !open__) +# { +# j_ = j +# j = j % l + 1 +# } +# } +# printf "#[fg=%s,bg=%s,none]%s", bg[j_], status_bg, mainsep +# }') +# fi +# +# status_left="$status_left " +# +# # -- status-right style +# +# tmux_conf_theme_status_right=${tmux_conf_theme_status_right-' #{prefix}#{mouse}#{pairing}#{synchronized}#{?battery_status, #{battery_status},}#{?battery_bar, #{battery_bar},}#{?battery_percentage, #{battery_percentage},} , %R , %d %b | #{username}#{root} | #{hostname} '} +# tmux_conf_theme_status_right_fg=${tmux_conf_theme_status_right_fg:-$tmux_conf_theme_colour_12,$tmux_conf_theme_colour_13,$tmux_conf_theme_colour_14} +# tmux_conf_theme_status_right_bg=${tmux_conf_theme_status_right_bg:-$tmux_conf_theme_colour_15,$tmux_conf_theme_colour_16,$tmux_conf_theme_colour_17} +# tmux_conf_theme_status_right_attr=${tmux_conf_theme_status_right_attr:-none,none,bold} +# +# if [ -n "$tmux_conf_theme_status_right" ]; then +# status_right=$(echo "$tmux_conf_theme_status_right" | sed \ +# -e "s/#{pairing}/#[fg=$tmux_conf_theme_pairing_fg]#[bg=$tmux_conf_theme_pairing_bg]#[$tmux_conf_theme_pairing_attr]#{?session_many_attached,$tmux_conf_theme_pairing ,}/g" \ +# -e "s/#{prefix}/#[fg=$tmux_conf_theme_prefix_fg]#[bg=$tmux_conf_theme_prefix_bg]#[$tmux_conf_theme_prefix_attr]#{?client_prefix,$tmux_conf_theme_prefix ,$(printf "$tmux_conf_theme_prefix" | sed -e 's/./ /g') }/g" \ +# -e "s/#{mouse}/#[fg=$tmux_conf_theme_mouse_fg]#[bg=$tmux_conf_theme_mouse_bg]#[$tmux_conf_theme_mouse_attr]#{?mouse,$tmux_conf_theme_mouse ,$(printf "$tmux_conf_theme_mouse" | sed -e 's/./ /g') }/g" \ +# -e "s%#{synchronized}%#[fg=$tmux_conf_theme_synchronized_fg]#[bg=$tmux_conf_theme_synchronized_bg]#[$tmux_conf_theme_synchronized_attr]#{?pane_synchronized,$tmux_conf_theme_synchronized ,}%g" \ +# -e 's%#{circled_session_name}%#(cut -c3- ~/.tmux.conf | sh -s _circled #S)%g') +# +# if [ -n "$(tmux display -p '#{version}')" ]; then +# status_right=$(echo "$status_right" | sed \ +# -e "s%#{root}%#[fg=$tmux_conf_theme_root_fg]#[bg=$tmux_conf_theme_root_bg]#[$tmux_conf_theme_root_attr]#{?#{==:#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} #D),root},$tmux_conf_theme_root,}#[inherit]%g") +# else +# status_right=$(echo "$status_right" | sed \ +# -e "s%#{root}%#[fg=$tmux_conf_theme_root_fg]#[bg=$tmux_conf_theme_root_bg]#[$tmux_conf_theme_root_attr]#(cut -c3- ~/.tmux.conf | sh -s _root #{pane_tty} $tmux_conf_theme_root #D)#[inherit]%g") +# fi +# +# status_right=$(printf '%s' "$status_right" | awk \ +# -v status_bg="$tmux_conf_theme_status_bg" \ +# -v fg_="$tmux_conf_theme_status_right_fg" \ +# -v bg_="$tmux_conf_theme_status_right_bg" \ +# -v attr_="$tmux_conf_theme_status_right_attr" \ +# -v mainsep="$tmux_conf_theme_right_separator_main" \ +# -v subsep="$tmux_conf_theme_right_separator_sub" ' +# function subsplit(s, l, i, a, r) +# { +# l = split(s, a, ",") +# for (i = 1; i <= l; ++i) +# { +# o = split(a[i], _, "(") - 1 +# c = split(a[i], _, ")") - 1 +# open += o - c +# o_ = split(a[i], _, "{") - 1 +# c_ = split(a[i], _, "}") - 1 +# open_ += o_ - c_ +# o__ = split(a[i], _, "[") - 1 +# c__ = split(a[i], _, "]") - 1 +# open__ += o__ - c__ +# +# if (i == l) +# r = sprintf("%s%s", r, a[i]) +# else if (open || open_ || open__) +# r = sprintf("%s%s,", r, a[i]) +# else +# r = sprintf("%s%s#[fg=%s,bg=%s,%s]%s", r, a[i], fg[j], bg[j], attr[j], subsep) +# } +# +# gsub(/#\[inherit\]/, sprintf("#[default]#[fg=%s,bg=%s,%s]", fg[j], bg[j], attr[j]), r) +# return r +# } +# BEGIN { +# FS = "|" +# l1 = split(fg_, fg, ",") +# l2 = split(bg_, bg, ",") +# l3 = split(attr_, attr, ",") +# l = l1 < l2 ? (l1 < l3 ? l1 : l3) : (l2 < l3 ? l2 : l3) +# } +# { +# for (i = j = 1; i <= NF; ++i) +# { +# if (open_ || open || open__) +# printf "|%s", subsplit($i) +# else +# printf "#[fg=%s,bg=%s,none]%s#[fg=%s,bg=%s,%s]%s", bg[j], (i == 1) ? status_bg : bg[j_], mainsep, fg[j], bg[j], attr[j], subsplit($i) +# +# if (!open && !open_ && !open__) +# { +# j_ = j +# j = j % l + 1 +# } +# } +# }') +# fi +# +# # -- variables +# +# tmux_conf_battery_bar_symbol_full=$(_decode_unicode_escapes "${tmux_conf_battery_bar_symbol_full:-◼}") +# tmux_conf_battery_bar_symbol_empty=$(_decode_unicode_escapes "${tmux_conf_battery_bar_symbol_empty:-◻}") +# tmux_conf_battery_bar_length=${tmux_conf_battery_bar_length:-auto} +# tmux_conf_battery_bar_palette=${tmux_conf_battery_bar_palette:-gradient} +# tmux_conf_battery_hbar_palette=${tmux_conf_battery_hbar_palette:-gradient} +# tmux_conf_battery_vbar_palette=${tmux_conf_battery_vbar_palette:-gradient} +# tmux_conf_battery_status_charging=$(_decode_unicode_escapes "${tmux_conf_battery_status_charging:-↑}") # U+2191 +# tmux_conf_battery_status_discharging=$(_decode_unicode_escapes "${tmux_conf_battery_status_discharging:-↓}") # U+2193 +# +# _pkillf 'cut -c3- ~/\.tmux\.conf \| sh -s _battery_bar' +# _battery_info +# if [ "$charge" != 0 ]; then +# case "$status_left $status_right" in +# *'#{battery_'*|*'#{?battery_'*) +# status_left=$(echo "$status_left" | sed -E \ +# -e 's/#\{(\?)?battery_bar/#\{\1@battery_bar/g' \ +# -e 's/#\{(\?)?battery_hbar/#\{\1@battery_hbar/g' \ +# -e 's/#\{(\?)?battery_vbar/#\{\1@battery_vbar/g' \ +# -e 's/#\{(\?)?battery_status/#\{\1@battery_status/g' \ +# -e 's/#\{(\?)?battery_percentage/#\{\1@battery_percentage/g') +# status_right=$(echo "$status_right" | sed -E \ +# -e 's/#\{(\?)?battery_bar/#\{\1@battery_bar/g' \ +# -e 's/#\{(\?)?battery_hbar/#\{\1@battery_hbar/g' \ +# -e 's/#\{(\?)?battery_vbar/#\{\1@battery_vbar/g' \ +# -e 's/#\{(\?)?battery_status/#\{\1@battery_status/g' \ +# -e 's/#\{(\?)?battery_percentage/#\{\1@battery_percentage/g') +# status_right="#(echo; nice cut -c3- ~/.tmux.conf | sh -s _battery_status \"$tmux_conf_battery_status_charging\" \"$tmux_conf_battery_status_discharging\")$status_right" +# interval=60 +# if [ $_tmux_version -ge 320 ]; then +# tmux run -b "trap 'exit 0' TERM; while :; do nice cut -c3- ~/.tmux.conf | sh -s _battery_bar \"$tmux_conf_battery_bar_symbol_full\" \"$tmux_conf_battery_bar_symbol_empty\" \"$tmux_conf_battery_bar_length\" \"$tmux_conf_battery_bar_palette\" \"$tmux_conf_battery_hbar_palette\" \"$tmux_conf_battery_vbar_palette\"; sleep $interval; done" +# elif [ $_tmux_version -gt 240 ]; then +# status_right="#(echo; while :; do nice cut -c3- ~/.tmux.conf | sh -s _battery_bar \"$tmux_conf_battery_bar_symbol_full\" \"$tmux_conf_battery_bar_symbol_empty\" \"$tmux_conf_battery_bar_length\" \"$tmux_conf_battery_bar_palette\" \"$tmux_conf_battery_hbar_palette\" \"$tmux_conf_battery_vbar_palette\"; sleep $interval; done)$status_right" +# else +# status_right="#(nice cut -c3- ~/.tmux.conf | sh -s _battery_bar \"$tmux_conf_battery_bar_symbol_full\" \"$tmux_conf_battery_bar_symbol_empty\" \"$tmux_conf_battery_bar_length\" \"$tmux_conf_battery_bar_palette\" \"$tmux_conf_battery_hbar_palette\" \"$tmux_conf_battery_vbar_palette\")$status_right" +# fi +# ;; +# esac +# fi +# +# case "$status_left $status_right" in +# *'#{username}'*|*'#{hostname}'*|*'#{hostname_full}'*|*'#{username_ssh}'*|*'#{hostname_ssh}'*|*'#{hostname_full_ssh}'*) +# status_left=$(echo "$status_left" | sed \ +# -e 's%#{username}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} false #D)%g' \ +# -e 's%#{hostname}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false false #h #D)%g' \ +# -e 's%#{hostname_full}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false true #H #D)%g' \ +# -e 's%#{username_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} true #D)%g' \ +# -e 's%#{hostname_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true false #h #D)%g' \ +# -e 's%#{hostname_full_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true true #H #D)%g') +# status_right=$(echo "$status_right" | sed \ +# -e 's%#{username}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} false #D)%g' \ +# -e 's%#{hostname}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false false #h #D)%g' \ +# -e 's%#{hostname_full}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} false true #H #D)%g' \ +# -e 's%#{username_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _username #{pane_tty} true #D)%g' \ +# -e 's%#{hostname_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true false #h #D)%g' \ +# -e 's%#{hostname_full_ssh}%#(cut -c3- ~/.tmux.conf | sh -s _hostname #{pane_tty} true true #H #D)%g') +# ;; +# esac +# +# _pkillf 'cut -c3- ~/\.tmux\.conf \| sh -s _uptime' +# case "$status_left $status_right" in +# *'#{uptime_'*|*'#{?uptime_'*) +# status_left=$(echo "$status_left" | sed -E \ +# -e 's/#\{(\?)?uptime_y/#\{\1@uptime_y/g' \ +# -e 's/#\{(\?)?uptime_d/#\{\1@uptime_d/g' \ +# -e '/@uptime_y/ s/@uptime_d/@uptime_dy/g' \ +# -e 's/#\{(\?)?uptime_h/#\{\1@uptime_h/g' \ +# -e 's/#\{(\?)?uptime_m/#\{\1@uptime_m/g' \ +# -e 's/#\{(\?)?uptime_s/#\{\1@uptime_s/g') +# status_right=$(echo "$status_right" | sed -E \ +# -e 's/#\{(\?)?uptime_y/#\{\1@uptime_y/g' \ +# -e 's/#\{(\?)?uptime_d/#\{\1@uptime_d/g' \ +# -e '/@uptime_y/ s/@uptime_d/@uptime_dy/g' \ +# -e 's/#\{(\?)?uptime_h/#\{\1@uptime_h/g' \ +# -e 's/#\{(\?)?uptime_m/#\{\1@uptime_m/g' \ +# -e 's/#\{(\?)?uptime_s/#\{\1@uptime_s/g') +# interval=60 +# case "$status_left $status_right" in +# *'#{@uptime_s}'*) +# interval=$(tmux show -gv status-interval) +# ;; +# esac +# if [ $_tmux_version -ge 320 ]; then +# tmux run -b "trap 'exit 0' TERM; while :; do nice cut -c3- ~/.tmux.conf | sh -s _uptime; sleep $interval; done" +# elif [ $_tmux_version -gt 240 ]; then +# status_right="#(echo; while :; do nice cut -c3- ~/.tmux.conf | sh -s _uptime; sleep $interval; done)$status_right" +# else +# status_right="#(nice cut -c3- ~/.tmux.conf | sh -s _uptime)$status_right" +# fi +# ;; +# esac +# +# _pkillf 'cut -c3- ~/\.tmux\.conf \| sh -s _loadavg' +# case "$status_left $status_right" in +# *'#{loadavg'*|*'#{?loadavg'*) +# status_left=$(echo "$status_left" | sed -E \ +# -e 's/#\{(\?)?loadavg/#\{\1@loadavg/g') +# status_right=$(echo "$status_right" | sed -E \ +# -e 's/#\{(\?)?loadavg/#\{\1@loadavg/g') +# interval=$(tmux show -gv status-interval) +# if [ $_tmux_version -ge 320 ]; then +# tmux run -b "trap 'exit 0' TERM; while :; do nice cut -c3- ~/.tmux.conf | sh -s _loadavg; sleep $interval; done" +# elif [ $_tmux_version -gt 240 ]; then +# status_right="#(echo; while :; do nice cut -c3- ~/.tmux.conf | sh -s _loadavg; sleep $interval; done)$status_right" +# else +# status_right="#(nice cut -c3- ~/.tmux.conf | sh -s _loadavg)$status_right" +# fi +# ;; +# esac +# +# # -- clock ------------------------------------------------------------- +# +# tmux_conf_theme_clock_colour=${tmux_conf_theme_clock_colour:-$tmux_conf_theme_colour_4} +# tmux_conf_theme_clock_style=${tmux_conf_theme_clock_style:-24} +# +# # -- custom variables --------------------------------------------------- +# +# if [ -f ~/.tmux.conf.local ] && [ x"$(cut -c3- ~/.tmux.conf.local | sh 2>/dev/null -s printf probe)" = x"probe" ]; then +# replacements=$(perl -n -e 'print if s!^#\s+([^()\s]+)\s*\(\)\s*{\s*\n!s%#\\\{\1\\\}%#(cut -c3- ~/.tmux.conf.local | sh -s \1)%g; !p' < ~/.tmux.conf.local) +# status_left=$(echo "$status_left" | perl -p -e "$replacements" || echo "$status_left") +# status_right=$(echo "$status_right" | perl -p -e "$replacements" || echo "$status_right") +# fi +# +# # ----------------------------------------------------------------------- +# +# tmux setw -g window-style "$window_style" \; setw -g window-active-style "$window_active_style" \;\ +# setw -g pane-border-style "fg=$tmux_conf_theme_pane_border_fg,bg=$tmux_conf_theme_pane_border_bg" \; set -g pane-active-border-style "fg=$tmux_conf_theme_pane_active_border_fg,bg=$tmux_conf_theme_pane_active_border_bg" \;\ +# set -g display-panes-colour "$tmux_conf_theme_pane_indicator" \; set -g display-panes-active-colour "$tmux_conf_theme_pane_active_indicator" \;\ +# set -g message-style "fg=$tmux_conf_theme_message_fg,bg=$tmux_conf_theme_message_bg,$tmux_conf_theme_message_attr" \;\ +# set -g message-command-style "fg=$tmux_conf_theme_message_command_fg,bg=$tmux_conf_theme_message_command_bg,$tmux_conf_theme_message_command_attr" \;\ +# setw -g mode-style "fg=$tmux_conf_theme_mode_fg,bg=$tmux_conf_theme_mode_bg,$tmux_conf_theme_mode_attr" \;\ +# set -g status-style "fg=$tmux_conf_theme_status_fg,bg=$tmux_conf_theme_status_bg,$tmux_conf_theme_status_attr" \;\ +# set -g status-left-style "fg=$tmux_conf_theme_status_fg,bg=$tmux_conf_theme_status_bg,$tmux_conf_theme_status_attr" \;\ +# set -g status-right-style "fg=$tmux_conf_theme_status_fg,bg=$tmux_conf_theme_status_bg,$tmux_conf_theme_status_attr" \;\ +# set -g set-titles-string "$(_decode_unicode_escapes "$tmux_conf_theme_terminal_title")" \;\ +# setw -g window-status-style "fg=$tmux_conf_theme_window_status_fg,bg=$tmux_conf_theme_window_status_bg,$tmux_conf_theme_window_status_attr" \;\ +# setw -g window-status-format "$(_decode_unicode_escapes "$tmux_conf_theme_window_status_format")" \;\ +# setw -g window-status-current-style "fg=$tmux_conf_theme_window_status_current_fg,bg=$tmux_conf_theme_window_status_current_bg,$tmux_conf_theme_window_status_current_attr" \;\ +# setw -g window-status-current-format "$(_decode_unicode_escapes "$tmux_conf_theme_window_status_current_format")" \;\ +# setw -g window-status-activity-style "fg=$tmux_conf_theme_window_status_activity_fg,bg=$tmux_conf_theme_window_status_activity_bg,$tmux_conf_theme_window_status_activity_attr" \;\ +# setw -g window-status-bell-style "fg=$tmux_conf_theme_window_status_bell_fg,bg=$tmux_conf_theme_window_status_bell_bg,$tmux_conf_theme_window_status_bell_attr" \;\ +# setw -g window-status-last-style "fg=$tmux_conf_theme_window_status_last_fg,bg=$tmux_conf_theme_window_status_last_bg,$tmux_conf_theme_window_status_last_attr" \;\ +# setw -g window-status-separator "$window_status_separator" \;\ +# set -g status-left-length 1000 \; set -g status-left "$(_decode_unicode_escapes "$status_left")" \;\ +# set -g status-right-length 1000 \; set -g status-right "$(_decode_unicode_escapes "$status_right")" \;\ +# setw -g clock-mode-colour "$tmux_conf_theme_clock_colour" \;\ +# setw -g clock-mode-style "$tmux_conf_theme_clock_style" +# } +# +# __apply_plugins() { +# window_active="$1" +# tmux_conf_update_plugins_on_launch="$2" +# tmux_conf_update_plugins_on_reload="$3" +# +# TMUX_PLUGIN_MANAGER_PATH=${TMUX_PLUGIN_MANAGER_PATH:-~/.tmux/plugins} +# if [ ! -d "$TMUX_PLUGIN_MANAGER_PATH/tpm" ]; then +# install_tpm=true +# tmux display 'Installing tpm and plugins...' +# git clone --depth 1 https://github.com/tmux-plugins/tpm "$TMUX_PLUGIN_MANAGER_PATH/tpm" +# elif { [ -z "$window_active" ] && _is_enabled "$tmux_conf_update_plugins_on_launch"; } || { [ -n "$window_active" ] && _is_enabled "$tmux_conf_update_plugins_on_reload"; }; then +# update_tpm=true +# tmux display 'Updating tpm and plugins...' +# (cd "$TMUX_PLUGIN_MANAGER_PATH/tpm" && git checkout -q master && git reset -q --hard HEAD && git pull -q origin master) +# fi +# if [ x"$install_tpm" = x"true" ] || [ x"$update_tpm" = x"true" ]; then +# perl -0777 -p -i -e 's/git clone --recursive/git clone --recursive --depth 1 --shallow-submodules/g +# ;s/(install_plugin "\$plugin")\n(\s+)done/\1&\n\2done\n\2wait/g' "$TMUX_PLUGIN_MANAGER_PATH/tpm/scripts/install_plugins.sh" +# perl -p -i -e 's/git submodule update --init --recursive/git submodule update --init --recursive --depth 1/g' "$TMUX_PLUGIN_MANAGER_PATH/tpm/scripts/update_plugin.sh" +# tmux set-environment -g TMUX_PLUGIN_MANAGER_PATH "$TMUX_PLUGIN_MANAGER_PATH" +# fi +# if [ x"$update_tpm" = x"true" ]; then +# "$TMUX_PLUGIN_MANAGER_PATH/tpm/bin/update_plugins" all >/dev/null 2>&1 +# tmux display 'Done updating tpm and plugins...' +# elif [ x"$install_tpm" = x"true" ]; then +# "$TMUX_PLUGIN_MANAGER_PATH/tpm/bin/install_plugins" >/dev/null 2>&1 +# tmux display 'Done installing tpm and plugins...' +# fi +# "$TMUX_PLUGIN_MANAGER_PATH/tpm/tpm" +# if [ -z "$window_active" ] && [ $_tmux_version -lt 240 ]; then +# tmux run -b "sleep $(expr $(tmux display -p '#{display-time}') / 500) && tmux set display-time 3000 \; display 'This configuration will soon require tmux 2.4+' \; set -u display-time" +# fi +# } +# +# _apply_plugins() { +# tmux_conf_update_plugins_on_launch=${tmux_conf_update_plugins_on_launch:-true} +# tmux_conf_update_plugins_on_reload=${tmux_conf_update_plugins_on_reload:-true} +# if [ -n "$(tmux show -gv '@plugin')" ] || [ -n "$(tmux show -gv '@tpm_plugins')" ]; then +# tmux run -b "cut -c3- ~/.tmux.conf | sh -s __apply_plugins \"$window_active\" \"$tmux_conf_update_plugins_on_launch\" \"$tmux_conf_update_plugins_on_reload\"" +# elif [ -z "$window_active" ]; then +# if [ $_tmux_version -lt 240 ]; then +# tmux run -b 'tmux set display-time 3000 \; display "This configuration will soon require tmux 2.4+" \; set -u display-time' +# fi +# fi +# } +# +# _apply_configuration() { +# +# window_active="$(tmux display -p '#{window_active}' 2>/dev/null || true)" +# if [ -z "$window_active" ]; then +# if ! command -v perl > /dev/null 2>&1; then +# tmux run -b 'tmux set display-time 3000 \; display "This configuration requires perl" \; set -u display-time \; run "sleep 3" \; kill-server' +# return +# fi +# if ! command -v sed > /dev/null 2>&1; then +# tmux run -b 'tmux set display-time 3000 \; display "This configuration requires sed" \; set -u display-time \; run "sleep 3" \; kill-server' +# return +# fi +# if ! command -v awk > /dev/null 2>&1; then +# tmux run -b 'tmux set display-time 3000 \; display "This configuration requires awk" \; set -u display-time \; run "sleep 3" \; kill-server' +# return +# fi +# if [ $_tmux_version -lt 230 ]; then +# tmux run -b 'tmux set display-time 3000 \; display "This configuration requires tmux 2.3+" \; set -u display-time \; run "sleep 3" \; kill-server' +# return +# fi +# fi +# +# # see https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard +# if command -v reattach-to-user-namespace > /dev/null 2>&1; then +# default_shell="$(tmux show -gv default-shell)" +# case "$default_shell" in +# *fish) +# tmux set -g default-command "reattach-to-user-namespace -l $default_shell" +# ;; +# *sh) +# tmux set -g default-command "exec $default_shell... 2> /dev/null & reattach-to-user-namespace -l $default_shell" +# ;; +# esac +# fi +# +# case "$_uname_s" in +# *CYGWIN*|*MSYS*) +# # prevent Cygwin and MSYS2 from cd-ing into home directory when evaluating /etc/profile +# tmux setenv -g CHERE_INVOKING 1 +# ;; +# esac +# +# _apply_overrides +# _apply_theme& +# _apply_bindings& +# +# # shellcheck disable=SC2046 +# tmux setenv -gu tmux_conf_dummy $(printenv | grep -E -o '^tmux_conf_[^=]+' | awk '{printf "; setenv -gu %s", $0}') +# wait +# +# _apply_plugins +# } +# +# _urlview() { +# tmux capture-pane -J -S - -E - -b "urlview-$1" -t "$1" +# tmux split-window "tmux show-buffer -b urlview-$1 | urlview || true; tmux delete-buffer -b urlview-$1" +# } +# +# _fpp() { +# tmux capture-pane -J -S - -E - -b "fpp-$1" -t "$1" +# tmux split-window "tmux show-buffer -b fpp-$1 | fpp || true; tmux delete-buffer -b fpp-$1" +# } +# +# "$@" diff --git a/tmux/.tmux.conf.local b/tmux/.tmux.conf.local new file mode 100644 index 0000000..057b2b2 --- /dev/null +++ b/tmux/.tmux.conf.local @@ -0,0 +1,406 @@ +# : << EOF +# https://github.com/gpakosz/.tmux +# (‑●‑●)> dual licensed under the WTFPL v2 license and the MIT license, +# without any warranty. +# Copyright 2012— Gregory Pakosz (@gpakosz). + + +# -- navigation ---------------------------------------------------------------- + +# if you're running tmux within iTerm2 +# - and tmux is 1.9 or 1.9a +# - and iTerm2 is configured to let option key act as +Esc +# - and iTerm2 is configured to send [1;9A -> [1;9D for option + arrow keys +# then uncomment the following line to make Meta + arrow keys mapping work +#set -ga terminal-overrides "*:kUP3=\e[1;9A,*:kDN3=\e[1;9B,*:kRIT3=\e[1;9C,*:kLFT3=\e[1;9D" + + +# -- windows & pane creation --------------------------------------------------- + +# new window retains current path, possible values are: +# - true +# - false (default) +tmux_conf_new_window_retain_current_path=false + +# new pane retains current path, possible values are: +# - true (default) +# - false +tmux_conf_new_pane_retain_current_path=true + +# new pane tries to reconnect ssh sessions (experimental), possible values are: +# - true +# - false (default) +tmux_conf_new_pane_reconnect_ssh=false + +# prompt for session name when creating a new session, possible values are: +# - true +# - false (default) +tmux_conf_new_session_prompt=false + + +# -- display ------------------------------------------------------------------- + +# RGB 24-bit colour support (tmux >= 2.2), possible values are: +# - true +# - false (default) +tmux_conf_24b_colour=false + +# default theme +tmux_conf_theme_colour_1="#080808" # dark gray +tmux_conf_theme_colour_2="#303030" # gray +tmux_conf_theme_colour_3="#8a8a8a" # light gray +tmux_conf_theme_colour_4="#00afff" # light blue +tmux_conf_theme_colour_5="#ffff00" # yellow +tmux_conf_theme_colour_6="#080808" # dark gray +tmux_conf_theme_colour_7="#e4e4e4" # white +tmux_conf_theme_colour_8="#080808" # dark gray +tmux_conf_theme_colour_9="#ffff00" # yellow +tmux_conf_theme_colour_10="#ff00af" # pink +tmux_conf_theme_colour_11="#5fff00" # green +tmux_conf_theme_colour_12="#8a8a8a" # light gray +tmux_conf_theme_colour_13="#e4e4e4" # white +tmux_conf_theme_colour_14="#080808" # dark gray +tmux_conf_theme_colour_15="#080808" # dark gray +tmux_conf_theme_colour_16="#d70000" # red +tmux_conf_theme_colour_17="#e4e4e4" # white + +# default theme (ansi) +#tmux_conf_theme_colour_1="colour0" +#tmux_conf_theme_colour_2="colour8" +#tmux_conf_theme_colour_3="colour8" +#tmux_conf_theme_colour_4="colour14" +#tmux_conf_theme_colour_5="colour11" +#tmux_conf_theme_colour_6="colour0" +#tmux_conf_theme_colour_7="colour15" +#tmux_conf_theme_colour_8="colour0" +#tmux_conf_theme_colour_9="colour11" +#tmux_conf_theme_colour_10="colour13" +#tmux_conf_theme_colour_11="colour10" +#tmux_conf_theme_colour_12="colour8" +#tmux_conf_theme_colour_13="colour15" +#tmux_conf_theme_colour_14="colour0" +#tmux_conf_theme_colour_15="colour0" +#tmux_conf_theme_colour_16="colour1" +#tmux_conf_theme_colour_17="colour15" + +# window style +tmux_conf_theme_window_fg="default" +tmux_conf_theme_window_bg="default" + +# highlight focused pane (tmux >= 2.1), possible values are: +# - true +# - false (default) +tmux_conf_theme_highlight_focused_pane=false + +# focused pane colours: +tmux_conf_theme_focused_pane_bg="$tmux_conf_theme_colour_2" + +# pane border style, possible values are: +# - thin (default) +# - fat +tmux_conf_theme_pane_border_style=thin + +# pane borders colours: +tmux_conf_theme_pane_border="$tmux_conf_theme_colour_2" +tmux_conf_theme_pane_active_border="$tmux_conf_theme_colour_4" + +# pane indicator colours (when you hit + q) +tmux_conf_theme_pane_indicator="$tmux_conf_theme_colour_4" +tmux_conf_theme_pane_active_indicator="$tmux_conf_theme_colour_4" + +# status line style +tmux_conf_theme_message_fg="$tmux_conf_theme_colour_1" +tmux_conf_theme_message_bg="$tmux_conf_theme_colour_5" +tmux_conf_theme_message_attr="bold" + +# status line command style ( : Escape) +tmux_conf_theme_message_command_fg="$tmux_conf_theme_colour_5" +tmux_conf_theme_message_command_bg="$tmux_conf_theme_colour_1" +tmux_conf_theme_message_command_attr="bold" + +# window modes style +tmux_conf_theme_mode_fg="$tmux_conf_theme_colour_1" +tmux_conf_theme_mode_bg="$tmux_conf_theme_colour_5" +tmux_conf_theme_mode_attr="bold" + +# status line style +tmux_conf_theme_status_fg="$tmux_conf_theme_colour_3" +tmux_conf_theme_status_bg="$tmux_conf_theme_colour_1" +tmux_conf_theme_status_attr="none" + +# terminal title +# - built-in variables are: +# - #{circled_window_index} +# - #{circled_session_name} +# - #{hostname} +# - #{hostname_ssh} +# - #{hostname_full} +# - #{hostname_full_ssh} +# - #{username} +# - #{username_ssh} +tmux_conf_theme_terminal_title="#h ❐ #S ● #I #W" + +# window status style +# - built-in variables are: +# - #{circled_window_index} +# - #{circled_session_name} +# - #{hostname} +# - #{hostname_ssh} +# - #{hostname_full} +# - #{hostname_full_ssh} +# - #{username} +# - #{username_ssh} +tmux_conf_theme_window_status_fg="$tmux_conf_theme_colour_3" +tmux_conf_theme_window_status_bg="$tmux_conf_theme_colour_1" +tmux_conf_theme_window_status_attr="none" +tmux_conf_theme_window_status_format="#I #W" +#tmux_conf_theme_window_status_format="#{circled_window_index} #W" +#tmux_conf_theme_window_status_format="#I #W#{?window_bell_flag,🔔,}#{?window_zoomed_flag,🔍,}" + +# window current status style +# - built-in variables are: +# - #{circled_window_index} +# - #{circled_session_name} +# - #{hostname} +# - #{hostname_ssh} +# - #{hostname_full} +# - #{hostname_full_ssh} +# - #{username} +# - #{username_ssh} +tmux_conf_theme_window_status_current_fg="$tmux_conf_theme_colour_1" +tmux_conf_theme_window_status_current_bg="$tmux_conf_theme_colour_4" +tmux_conf_theme_window_status_current_attr="bold" +tmux_conf_theme_window_status_current_format="#I #W" +#tmux_conf_theme_window_status_current_format="#{circled_window_index} #W" +#tmux_conf_theme_window_status_current_format="#I #W#{?window_zoomed_flag,🔍,}" + +# window activity status style +tmux_conf_theme_window_status_activity_fg="default" +tmux_conf_theme_window_status_activity_bg="default" +tmux_conf_theme_window_status_activity_attr="underscore" + +# window bell status style +tmux_conf_theme_window_status_bell_fg="$tmux_conf_theme_colour_5" +tmux_conf_theme_window_status_bell_bg="default" +tmux_conf_theme_window_status_bell_attr="blink,bold" + +# window last status style +tmux_conf_theme_window_status_last_fg="$tmux_conf_theme_colour_4" +tmux_conf_theme_window_status_last_bg="$tmux_conf_theme_colour_2" +tmux_conf_theme_window_status_last_attr="none" + +# status left/right sections separators +tmux_conf_theme_left_separator_main="" +tmux_conf_theme_left_separator_sub="|" +tmux_conf_theme_right_separator_main="" +tmux_conf_theme_right_separator_sub="|" +#tmux_conf_theme_left_separator_main='\uE0B0' # /!\ you don't need to install Powerline +#tmux_conf_theme_left_separator_sub='\uE0B1' # you only need fonts patched with +#tmux_conf_theme_right_separator_main='\uE0B2' # Powerline symbols or the standalone +#tmux_conf_theme_right_separator_sub='\uE0B3' # PowerlineSymbols.otf font, see README.md + +# status left/right content: +# - separate main sections with "|" +# - separate subsections with "," +# - built-in variables are: +# - #{battery_bar} +# - #{battery_hbar} +# - #{battery_percentage} +# - #{battery_status} +# - #{battery_vbar} +# - #{circled_session_name} +# - #{hostname_ssh} +# - #{hostname} +# - #{hostname_full} +# - #{hostname_full_ssh} +# - #{loadavg} +# - #{mouse} +# - #{pairing} +# - #{prefix} +# - #{root} +# - #{synchronized} +# - #{uptime_y} +# - #{uptime_d} (modulo 365 when #{uptime_y} is used) +# - #{uptime_h} +# - #{uptime_m} +# - #{uptime_s} +# - #{username} +# - #{username_ssh} +tmux_conf_theme_status_left=" ❐ #S | ↑#{?uptime_y, #{uptime_y}y,}#{?uptime_d, #{uptime_d}d,}#{?uptime_h, #{uptime_h}h,}#{?uptime_m, #{uptime_m}m,} " +tmux_conf_theme_status_right=" #{prefix}#{mouse}#{pairing}#{synchronized}#{?battery_status,#{battery_status},}#{?battery_bar, #{battery_bar},}#{?battery_percentage, #{battery_percentage},} , %R , %d %b | #{username}#{root} | #{hostname} " + +# status left style +tmux_conf_theme_status_left_fg="$tmux_conf_theme_colour_6,$tmux_conf_theme_colour_7,$tmux_conf_theme_colour_8" +tmux_conf_theme_status_left_bg="$tmux_conf_theme_colour_9,$tmux_conf_theme_colour_10,$tmux_conf_theme_colour_11" +tmux_conf_theme_status_left_attr="bold,none,none" + +# status right style +tmux_conf_theme_status_right_fg="$tmux_conf_theme_colour_12,$tmux_conf_theme_colour_13,$tmux_conf_theme_colour_14" +tmux_conf_theme_status_right_bg="$tmux_conf_theme_colour_15,$tmux_conf_theme_colour_16,$tmux_conf_theme_colour_17" +tmux_conf_theme_status_right_attr="none,none,bold" + +# pairing indicator +tmux_conf_theme_pairing="⚇" # U+2687 +tmux_conf_theme_pairing_fg="none" +tmux_conf_theme_pairing_bg="none" +tmux_conf_theme_pairing_attr="none" + +# prefix indicator +tmux_conf_theme_prefix="⌨" # U+2328 +tmux_conf_theme_prefix_fg="none" +tmux_conf_theme_prefix_bg="none" +tmux_conf_theme_prefix_attr="none" + +# mouse indicator +tmux_conf_theme_mouse="↗" # U+2197 +tmux_conf_theme_mouse_fg="none" +tmux_conf_theme_mouse_bg="none" +tmux_conf_theme_mouse_attr="none" + +# root indicator +tmux_conf_theme_root="!" +tmux_conf_theme_root_fg="none" +tmux_conf_theme_root_bg="none" +tmux_conf_theme_root_attr="bold,blink" + +# synchronized indicator +tmux_conf_theme_synchronized="⚏" # U+268F +tmux_conf_theme_synchronized_fg="none" +tmux_conf_theme_synchronized_bg="none" +tmux_conf_theme_synchronized_attr="none" + +# battery bar symbols +tmux_conf_battery_bar_symbol_full="◼" +tmux_conf_battery_bar_symbol_empty="◻" +#tmux_conf_battery_bar_symbol_full="♥" +#tmux_conf_battery_bar_symbol_empty="·" + +# battery bar length (in number of symbols), possible values are: +# - auto +# - a number, e.g. 5 +tmux_conf_battery_bar_length="auto" + +# battery bar palette, possible values are: +# - gradient (default) +# - heat +# - "colour_full_fg,colour_empty_fg,colour_bg" +tmux_conf_battery_bar_palette="gradient" +#tmux_conf_battery_bar_palette="#d70000,#e4e4e4,#000000" # red, white, black + +# battery hbar palette, possible values are: +# - gradient (default) +# - heat +# - "colour_low,colour_half,colour_full" +tmux_conf_battery_hbar_palette="gradient" +#tmux_conf_battery_hbar_palette="#d70000,#ff5f00,#5fff00" # red, orange, green + +# battery vbar palette, possible values are: +# - gradient (default) +# - heat +# - "colour_low,colour_half,colour_full" +tmux_conf_battery_vbar_palette="gradient" +#tmux_conf_battery_vbar_palette="#d70000,#ff5f00,#5fff00" # red, orange, green + +# symbols used to indicate whether battery is charging or discharging +tmux_conf_battery_status_charging="↑" # U+2191 +tmux_conf_battery_status_discharging="↓" # U+2193 +#tmux_conf_battery_status_charging="🔌" # U+1F50C +#tmux_conf_battery_status_discharging="🔋" # U+1F50B + +# clock style (when you hit + t) +# you may want to use %I:%M %p in place of %R in tmux_conf_theme_status_right +tmux_conf_theme_clock_colour="$tmux_conf_theme_colour_4" +tmux_conf_theme_clock_style="24" + + +# -- clipboard ----------------------------------------------------------------- + +# in copy mode, copying selection also copies to the OS clipboard +# - true +# - false (default) +# on macOS, this requires installing reattach-to-user-namespace, see README.md +# on Linux, this requires xsel or xclip +tmux_conf_copy_to_os_clipboard=false + + +# -- user customizations ------------------------------------------------------- +# this is the place to override or undo settings + +# increase history size +#set -g history-limit 10000 + +# start with mouse mode enabled +#set -g mouse on + +# force Vi mode +# really you should export VISUAL or EDITOR environment variable, see manual +#set -g status-keys vi +#set -g mode-keys vi + +# replace C-b by C-a instead of using both prefixes +# set -gu prefix2 +# unbind C-a +# unbind C-b +# set -g prefix C-a +# bind C-a send-prefix + +# move status line to top +#set -g status-position top + +# -- tpm ----------------------------------------------------------------------- + +# while I don't use tpm myself, many people requested official support so here +# is a seamless integration that automatically installs plugins in parallel + +# whenever a plugin introduces a variable to be used in 'status-left' or +# 'status-right', you can use it in 'tmux_conf_theme_status_left' and +# 'tmux_conf_theme_status_right' variables. + +# by default, launching tmux will update tpm and all plugins +# - true (default) +# - false +tmux_conf_update_plugins_on_launch=true + +# by default, reloading the configuration will update tpm and all plugins +# - true (default) +# - false +tmux_conf_update_plugins_on_reload=true + +# /!\ do not add set -g @plugin 'tmux-plugins/tpm' +# /!\ do not add run '~/.tmux/plugins/tpm/tpm' + +# to enable a plugin, use the 'set -g @plugin' syntax: +# visit https://github.com/tmux-plugins for available plugins +#set -g @plugin 'tmux-plugins/tmux-copycat' +#set -g @plugin 'tmux-plugins/tmux-cpu' +#set -g @plugin 'tmux-plugins/tmux-resurrect' +#set -g @plugin 'tmux-plugins/tmux-continuum' +#set -g @continuum-restore 'on' + + +# -- custom variables ---------------------------------------------------------- + +# to define a custom #{foo} variable, define a POSIX shell function between the +# '# EOF' and the '# "$@"' lines. Please note that the opening brace { character +# must be on the same line as the function name otherwise the parse won't detect +# it. +# +# then, use #{foo} in e.g. the 'tmux_conf_theme_status_left' or the +# 'tmux_conf_theme_status_right' variables. + +# # /!\ do not remove the following line +# EOF +# +# # /!\ do not "uncomment" the functions: the leading "# " characters are needed +# +# weather() { +# curl -m 1 wttr.in?format=3 2>/dev/null +# sleep 900 # sleep for 15 minutes, throttle network requests whatever the value of status-interval +# } +# +# online() { +# ping -c 1 1.1.1.1 >/dev/null 2>&1 && printf '✔' || printf '✘' +# } +# +# "$@" +# # /!\ do not remove the previous line From 86df08308a81e668b43630c2d5a89dec779f63d3 Mon Sep 17 00:00:00 2001 From: laujonat Date: Wed, 18 Aug 2021 21:38:29 -0700 Subject: [PATCH 09/25] tmux link --- bash/bash_profile | 4 ++++ bash/profile | 5 +++++ install | 52 +++++++++++++++++++++++++++-------------------- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/bash/bash_profile b/bash/bash_profile index 94ea667..884aa73 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -28,3 +28,7 @@ if [ -z "$SSH_AUTH_SOCK" -a -x "$SSHAGENT" ]; then eval `$SSHAGENT $SSHAGENTARGS` trap "kill $SSH_AGENT_PID" 0 fi +if [ -e /home/tlcie/.nix-profile/etc/profile.d/nix.sh ]; then . /home/tlcie/.nix-profile/etc/profile.d/nix.sh; fi # added by Nix installer + + +export PATH=$PATH:"/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code/bin" diff --git a/bash/profile b/bash/profile index 3c0488b..8ae91d1 100644 --- a/bash/profile +++ b/bash/profile @@ -23,3 +23,8 @@ export GOPATH PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting PATH="$PATH:/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code" + +if [ -d "$HOME/.local/bin" ] ; then + PATH="$HOME/.local/bin:$PATH" +fi + diff --git a/install b/install index 56ba32e..6891835 100755 --- a/install +++ b/install @@ -1,10 +1,10 @@ #!/usr/bin/env bash #set -e -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" echo "Repository is located at $DIR" cd $DIR -export REPO="`git rev-parse --show-toplevel`" +export REPO="$(git rev-parse --show-toplevel)" export BASH_DIR=$REPO/bash export SCRIPTS=$BASH_DIR/scripts @@ -31,12 +31,11 @@ function script_init() { function generate_config() { echo -e "\nGenerating sources from $SCRIPTS" - echo "alias dotfiles='cd $DIR'" >> $REPO/$BASH_SRC - echo "alias srcbash='. $DIR/install'" >> $REPO/$BASH_SRC - for f in $SCRIPTS/* - do + echo "alias dotfiles='cd $DIR'" >>$REPO/$BASH_SRC + echo "alias srcbash='. $DIR/install'" >>$REPO/$BASH_SRC + for f in $SCRIPTS/*; do echo $f - cat $f >> $BASH_SRC + cat $f >>$BASH_SRC done os_patch echo "Done." @@ -44,35 +43,44 @@ function generate_config() { # Path mismatches in supported command flags function os_patch() { - OS="`uname -s`" + OS="$(uname -s)" ARCH="uname -m" case $OS in - 'Linux') - OS='Linux' - echo "alias ls='ls --color=auto'" >> $BASH_SRC - ;; - 'FreeBSD') - OS='FreeBSD' - echo "alias ls='ls -G'" >> $BASH_SRC - ;; - 'Darwin') - OS='Mac' - ;; - *) ;; + 'Linux') + OS='Linux' + echo "alias ls='ls --color=auto'" >>$BASH_SRC + ;; + 'FreeBSD') + OS='FreeBSD' + echo "alias ls='ls -G'" >>$BASH_SRC + ;; + 'Darwin') + OS='Mac' + ;; + *) ;; esac } function link_bash() { - for f in $BASH_DIR/* - do + for f in $BASH_DIR/*; do FILENAME=$(basename $f) [[ -f $f ]] && ln -fsn -v $f $HOME/.$FILENAME done } +function link_tmux() { + if [ -d "$dir/.tmux" ]; then :; else + echo -e "\nFound tmux configuration. Overwriting tmux conf files." + eval ln -s -f $dir/tmux/.tmux.conf ~/.tmux.conf + eval cp $dir/tmux/.tmux.conf.local ~/.tmux.conf.local + fi + +} + init echo '------ Symlinking... ------' link_bash +link_tmux echo '------ Done ------' # source $BASH_DIR/bash_profile From f1dae97e9e6cfa6517be735caf9bc11dc7dbd2d6 Mon Sep 17 00:00:00 2001 From: laujonat Date: Thu, 9 Sep 2021 03:43:32 -0700 Subject: [PATCH 10/25] zsh --- zsh/.zshrc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ zsh/README.md | 3 +++ zsh/install | 8 ++++++++ 3 files changed, 57 insertions(+) create mode 100644 zsh/.zshrc create mode 100644 zsh/README.md create mode 100755 zsh/install diff --git a/zsh/.zshrc b/zsh/.zshrc new file mode 100644 index 0000000..44fabcc --- /dev/null +++ b/zsh/.zshrc @@ -0,0 +1,46 @@ + +# Time the stuff. +integer t0=$(date '+%s') + +source $HOME/antigen.zsh + +# Load the oh-my-zsh's library. +antigen use oh-my-zsh + +# Bundles from the default repo (robbyrussell's oh-my-zsh). +antigen bundle git +antigen bundle heroku +antigen bundle pip +antigen bundle docker +antigen bundle kubernetes +antigen bundle lein +antigen bundle fzf +antigen bundle gatsby +antigen bundle httpie +antigen bundle mosh +antigen bundle npm +antigen bundle copyfile +antigen bundle copydir +antigen bundle yarn +antigen bundle alias-finder +antigen bundle command-not-found +antigen bundle zsh-users/zsh-autosuggestions + +# Syntax highlighting bundle. +antigen bundle zsh-users/zsh-syntax-highlighting + +# Load the theme. +antigen theme robbyrussell + +# Tell Antigen that you're done. +antigen apply + + +function { + local -i t1 startup + t1=$(date '+%s') + startup=$(( t1 - t0 )) + [[ $startup -gt 1 ]] && print "Hmm, poor shell startup time: $startup" + ##print "startup time: $startup" +} +unset t0 diff --git a/zsh/README.md b/zsh/README.md new file mode 100644 index 0000000..44a586c --- /dev/null +++ b/zsh/README.md @@ -0,0 +1,3 @@ +`$ chmod +x install` +`$ ./install` + diff --git a/zsh/install b/zsh/install new file mode 100755 index 0000000..86d3601 --- /dev/null +++ b/zsh/install @@ -0,0 +1,8 @@ +#!/bin/bash + +curl -L git.io/antigen > antigen.zsh + +git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf +~/.fzf/install + + From cf446c69a78ebead9abb203e875aaea992fec5b7 Mon Sep 17 00:00:00 2001 From: laujonat Date: Thu, 10 Feb 2022 23:02:24 -0800 Subject: [PATCH 11/25] docker alias and go script --- docker | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker diff --git a/docker b/docker new file mode 100644 index 0000000..ee5024b --- /dev/null +++ b/docker @@ -0,0 +1,12 @@ +#!/bin/bash + + +ports() { + netstat -tulpn | grep LISTEN +} + +docker-ips() { + docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}} %tab% {{.Name}}' $(docker ps -aq) | sed 's#%tab%#\t#g' | sed 's#/##g' | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n +} + + From 6218ffe97696651c9db1fd43d508d267c575e84d Mon Sep 17 00:00:00 2001 From: laujonat Date: Fri, 11 Feb 2022 22:19:40 -0800 Subject: [PATCH 12/25] include neovim installation --- .gitignore | 6 + bash/bash_profile | 3 + bash/bashrc | 8 +- bash/profile | 6 +- bash/{scripts => utils}/aliases | 0 bash/{scripts => utils}/colors | 0 bash/{scripts => utils}/git | 0 bash/{scripts => utils}/network_utils | 0 bash/{scripts => utils}/process_utils | 0 bash/{scripts => utils}/prompt | 0 install | 2 +- scripts/install-in-ubuntu.sh | 18 + scripts/install_go.sh | 9 + vim/README.md | 0 vim/autoload/.gitignore | 1 + vim/autoload/plug.vim | 2801 +++++++++++++++++++++++++ vim/autoload/plug.vim.old | 2797 ++++++++++++++++++++++++ vim/config/.gitignore | 1 + vim/config/general.vimrc | 138 ++ vim/config/init.vimrc | 81 + vim/config/keys.vimrc | 136 ++ vim/config/line.vimrc | 53 + vim/config/plugins.vimrc | 127 ++ vim/init.vim | 5 + 24 files changed, 6183 insertions(+), 9 deletions(-) rename bash/{scripts => utils}/aliases (100%) rename bash/{scripts => utils}/colors (100%) rename bash/{scripts => utils}/git (100%) rename bash/{scripts => utils}/network_utils (100%) rename bash/{scripts => utils}/process_utils (100%) rename bash/{scripts => utils}/prompt (100%) create mode 100644 scripts/install-in-ubuntu.sh create mode 100644 scripts/install_go.sh create mode 100644 vim/README.md create mode 100644 vim/autoload/.gitignore create mode 100644 vim/autoload/plug.vim create mode 100644 vim/autoload/plug.vim.old create mode 100644 vim/config/.gitignore create mode 100644 vim/config/general.vimrc create mode 100644 vim/config/init.vimrc create mode 100644 vim/config/keys.vimrc create mode 100644 vim/config/line.vimrc create mode 100644 vim/config/plugins.vimrc create mode 100644 vim/init.vim diff --git a/.gitignore b/.gitignore index 4e04ee3..214b549 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ history/* /sandbox scripts/.fzf* bash/scripts/.nvimlog + +backup/ +plugged/ +undodir/ +fonts/ +.netrwhist diff --git a/bash/bash_profile b/bash/bash_profile index 884aa73..0cf3599 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -32,3 +32,6 @@ if [ -e /home/tlcie/.nix-profile/etc/profile.d/nix.sh ]; then . /home/tlcie/.nix export PATH=$PATH:"/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code/bin" + +export PATH="$PATH:/home/tlcie/.local/bin" + diff --git a/bash/bashrc b/bash/bashrc index 1547ec4..b4a0541 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -51,11 +51,6 @@ export NVM_DIR="$HOME/.nvm" export ZEPHYR_TOOLCHAIN_VARIANT=zephyr export ZEPHYR_SDK_INSTALL_DIR=/home/tlcie/.local/zephyr-sdk-0.11.2 -export GOROOT=/usr/local/go -export GOPATH=$HOME/go -export PATH=$PATH:$GOROOT/bin:$GOPATH/bin -PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting - # The next line updates PATH for the Google Cloud SDK. # if [ -f '/home/tlcie/google-cloud-sdk/path.bash.inc' ]; then . '/home/tlcie/google-cloud-sdk/path.bash.inc'; fi @@ -63,3 +58,6 @@ PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting # if [ -f '/home/tlcie/google-cloud-sdk/completion.bash.inc' ]; then . '/home/tlcie/google-cloud-sdk/completion.bash.inc'; fi export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH" +export GOROOT=/usr/local/go +export GOPATH=$HOME/go +export PATH=$GOPATH/bin:$GOROOT/bin:$PATH diff --git a/bash/profile b/bash/profile index 8ae91d1..6892bee 100644 --- a/bash/profile +++ b/bash/profile @@ -18,9 +18,9 @@ fi PATH="$HOME/.npm-global/bin:$PATH" -GOPATH="$HOME/.go" -export GOPATH -PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting +# GOPATH="$HOME/.go" +# export GOPATH +# PATH=$PATH:$GOPATH/bin # Add GOPATH/bin to PATH for scripting PATH="$PATH:/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code" diff --git a/bash/scripts/aliases b/bash/utils/aliases similarity index 100% rename from bash/scripts/aliases rename to bash/utils/aliases diff --git a/bash/scripts/colors b/bash/utils/colors similarity index 100% rename from bash/scripts/colors rename to bash/utils/colors diff --git a/bash/scripts/git b/bash/utils/git similarity index 100% rename from bash/scripts/git rename to bash/utils/git diff --git a/bash/scripts/network_utils b/bash/utils/network_utils similarity index 100% rename from bash/scripts/network_utils rename to bash/utils/network_utils diff --git a/bash/scripts/process_utils b/bash/utils/process_utils similarity index 100% rename from bash/scripts/process_utils rename to bash/utils/process_utils diff --git a/bash/scripts/prompt b/bash/utils/prompt similarity index 100% rename from bash/scripts/prompt rename to bash/utils/prompt diff --git a/install b/install index 6891835..47cbcac 100755 --- a/install +++ b/install @@ -6,7 +6,7 @@ echo "Repository is located at $DIR" cd $DIR export REPO="$(git rev-parse --show-toplevel)" export BASH_DIR=$REPO/bash -export SCRIPTS=$BASH_DIR/scripts +export SCRIPTS=$BASH_DIR/utils function init() { echo $SCRIPTS diff --git a/scripts/install-in-ubuntu.sh b/scripts/install-in-ubuntu.sh new file mode 100644 index 0000000..5787b10 --- /dev/null +++ b/scripts/install-in-ubuntu.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# For Ubuntu 18 +CONF_DIR=~/deps/nvim +mkdir -p ~/deps + +sudo apt install -y software-properties-common +sudo add-apt-repository ppa:neovim-ppa/stable +sudo apt update -y +sudo apt install -y neovim +sudo apt install -y python-dev python-pip python3-dev python3-pip +sudo apt install -y git # for clone +sudo apt autoremove -y + +git clone https://github.com/hiyali/nvim $CONF_DIR +mkdir -p ~/.config +ln -s $CONF_DIR ~/.config/nvim + +sudo update-alternatives --config editor diff --git a/scripts/install_go.sh b/scripts/install_go.sh new file mode 100644 index 0000000..1cde1d9 --- /dev/null +++ b/scripts/install_go.sh @@ -0,0 +1,9 @@ +VERSION=1.17 +OS=linux +ARCH=amd64 + +cd $HOME +wget https://storage.googleapis.com/golang/go$VERSION.$OS-$ARCH.tar.gz +tar -xvf go$VERSION.$OS-$ARCH.tar.gz +mv go go-$VERSION +sudo mv go-$VERSION /usr/local diff --git a/vim/README.md b/vim/README.md new file mode 100644 index 0000000..e69de29 diff --git a/vim/autoload/.gitignore b/vim/autoload/.gitignore new file mode 100644 index 0000000..e774117 --- /dev/null +++ b/vim/autoload/.gitignore @@ -0,0 +1 @@ +!plug.vim diff --git a/vim/autoload/plug.vim b/vim/autoload/plug.vim new file mode 100644 index 0000000..6a958cb --- /dev/null +++ b/vim/autoload/plug.vim @@ -0,0 +1,2801 @@ +" vim-plug: Vim plugin manager +" ============================ +" +" Download plug.vim and put it in ~/.vim/autoload +" +" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" +" Edit your .vimrc +" +" call plug#begin('~/.vim/plugged') +" +" " Make sure you use single quotes +" +" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align +" Plug 'junegunn/vim-easy-align' +" +" " Any valid git URL is allowed +" Plug 'https://github.com/junegunn/vim-github-dashboard.git' +" +" " Multiple Plug commands can be written in a single line using | separators +" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' +" +" " On-demand loading +" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } +" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } +" +" " Using a non-default branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } +" +" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) +" Plug 'fatih/vim-go', { 'tag': '*' } +" +" " Plugin options +" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } +" +" " Plugin outside ~/.vim/plugged with post-update hook +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +" +" " Unmanaged plugin (manually installed and updated) +" Plug '~/my-prototype-plugin' +" +" " Initialize plugin system +" call plug#end() +" +" Then reload .vimrc and :PlugInstall to install plugins. +" +" Plug options: +" +"| Option | Description | +"| ----------------------- | ------------------------------------------------ | +"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | +"| `rtp` | Subdirectory that contains Vim plugin | +"| `dir` | Custom directory for the plugin | +"| `as` | Use different name for the plugin | +"| `do` | Post-update hook (string or funcref) | +"| `on` | On-demand loading: Commands or ``-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand(':p')) + set shellslash +else + let s:me = resolve(expand(':p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! s:is_powershell(shell) + return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$' +endfunction + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + if has('win32') + \ && &shellslash + \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell)) + return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') + endif + if !has('nvim') + \ && (has('win32') || has('win32unix')) + \ && !has('multi_byte') + return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) + command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) + command! -nargs=0 -bar -bang PlugClean call s:clean(0) + 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! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('plug#end() called without calling plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') + let s:loaded[name] = 1 + continue + endif + + if has_key(plug, 'on') + let s:triggers[name] = { 'map': [], 'cmd': [] } + for cmd in s:to_a(plug.on) + if cmd =~? '^.\+' + 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 + +function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) +endfunction + +if s:is_win + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction + + " Copied from fzf + function! s:wrap_cmds(cmds) + let cmds = [ + \ '@echo off', + \ 'setlocal enabledelayedexpansion'] + \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) + \ + ['endlocal'] + if has('iconv') + if !exists('s:codepage') + let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) + endif + return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) + endif + return map(cmds, 'v:val."\r"') + endfunction + + function! s:batchfile(cmd) + let batchfile = s:plug_tempname().'.bat' + call writefile(s:wrap_cmds(a:cmd), batchfile) + let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) + endif +endfunction + +function! s:dobufread(names) + for name in a:names + let path = s:rtp(g:plugs[name]) + for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] + if len(finddir(dir, path)) + if exists('#BufRead') + doautocmd BufRead + endif + return + endif + endfor + endfor +endfunction + +function! plug#load(...) + if a:0 == 0 + return s:err('Argument missing: plugin name(s) required') + endif + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 + let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') + if !empty(unknowns) + let s = len(unknowns) > 1 ? 's' : '' + return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) + end + let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') + if !empty(unloaded) + for name in unloaded + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + call s:dobufread(unloaded) + return 1 + end + return 0 +endfunction + +function! s:remove_triggers(name) + if !has_key(s:triggers, a:name) + return + endif + for cmd in s:triggers[a:name].cmd + execute 'silent! delc' cmd + endfor + for map in s:triggers[a:name].map + execute 'silent! unmap' map + execute 'silent! iunmap' map + endfor + call remove(s:triggers, a:name) +endfunction + +function! s:lod(names, types, ...) + for name in a:names + call s:remove_triggers(name) + let s:loaded[name] = 1 + endfor + call s:reorg_rtp() + + for name in a:names + let rtp = s:rtp(g:plugs[name]) + for dir in a:types + call s:source(rtp, dir.'/**/*.vim') + endfor + if a:0 + if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) + execute 'runtime' a:1 + endif + call s:source(rtp, a:2) + endif + call s:doautocmd('User', name) + endfor +endfunction + +function! s:lod_ft(pat, names) + let syn = 'syntax/'.a:pat.'.vim' + call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) + execute 'autocmd! PlugLOD FileType' a:pat + call s:doautocmd('filetypeplugin', 'FileType') + call s:doautocmd('filetypeindent', 'FileType') +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) +endfunction + +function! s:lod_map(map, names, with_prefix, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + + if a:with_prefix + let prefix = v:count ? v:count : '' + let prefix .= '"'.v:register.a:prefix + if mode(1) == 'no' + if v:operator == 'c' + let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(repo . ' ' . v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' + if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif + let opts.tag = a:arg + elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-\{1}\ / + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap 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(s:plug_getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap q :call close_pane() + 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 + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + else + bd + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win + set shell=sh + endif + if a:swap + if s:is_powershell(&shell) + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' + set shellredir=>%s\ 2>&1 + endif + endif + return prev +endfunction + +function! s:bang(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') + execute "normal! :execute g:_plug_bang\\" + finally + unlet g:_plug_bang + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + call s:load_plugin(spec) + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:git_revision(a:spec.dir) + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' + let output = s:system( + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = [] + if get(g:, 'plug_shallow', 1) + call extend(s:clone_opt, ['--depth', '1']) + if s:git_version_requirement(1, 7, 10) + call add(s:clone_opt, '--no-single-branch') + endif + endif + + if has('win32unix') || has('wsl') + call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = s:git_origin_branch(spec) + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return (a:event == 'stdout' || a:event == 'stderr') ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + + if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = a:cmd + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = s:plug_call('jobstart', argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) + if has_key(a:opts, 'dir') + let cmd = s:with_cd(cmd, a:opts.dir, 0) + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'err_mode': 'raw', + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + let max = line('$') + for i in range(4, max > 4 ? max : 4) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, max > 5 ? max : 5) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = empty(globpath(spec.dir, '.git', 1)) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, cmd, { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + let cmd = ['git', 'clone'] + if !has_tag + call extend(cmd, s:clone_opt) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt').join(' ') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') +endfunction + +function! s:shellesc_ps1(arg) + return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" +endfunction + +function! s:shellesc_sh(arg) + return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" +endfunction + +" Escape the shell argument based on the shell. +" Vim and Neovim's shellescape() are insufficient. +" 1. shellslash determines whether to use single/double quotes. +" Double-quote escaping is fragile for cmd.exe. +" 2. It does not work for powershell. +" 3. It does not work for *sh shells if the command is executed +" via cmd.exe (ie. cmd.exe /c sh -c command command_args) +" 4. It does not support batchfile syntax. +" +" Accepts an optional dictionary with the following keys: +" - shell: same as Vim/Neovim 'shell' option. +" If unset, fallback to 'cmd.exe' on Windows or 'sh'. +" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. +function! plug#shellescape(arg, ...) + if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' + return a:arg + endif + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\(\.exe\)\?$' + return s:shellesc_cmd(a:arg, script) + elseif s:is_powershell(shell) + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) +endfunction + +function! s:system(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + if type(a:cmd) == s:TYPE.list + " Neovim's system() supports list argument to bypass the shell + " but it cannot set the working directory for the command. + " Assume that the command does not rely on the shell. + if has('nvim') && a:0 == 0 + return system(a:cmd) + endif + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd + endif + if a:0 > 0 + let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) + endif + if s:is_win && type(a:cmd) != s:TYPE.list + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] + let remote = result[-1] + if empty(remote) + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let sha = s:git_revision(a:spec.dir) + if empty(sha) + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let current_branch = result[0] + " Check tag + let origin_branch = s:git_origin_branch(a:spec) + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif origin_branch !=# current_branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ current_branch, origin_branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ origin_branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap 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 + let err_count = 0 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + let err = s:rm_rf(line[2:]) + setlocal modifiable + if empty(err) + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + else + delete _ + call append(l1 - 1, s:format_message('x', line[1:], err)) + let l2 += len(s:lines(err)) + let err_count += 1 + endif + let msg = printf('Removed %d directories.', s:clean_count) + if err_count > 0 + let msg .= printf(' Failed to remove %d directories.', err_count) + endif + call setline(4, msg) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) + if v:shell_error + return s:err('Error upgrading vim-plug: '. out) + endif + + if readfile(s:me) ==# readfile(new) + echo 'vim-plug is already up-to-date' + return 0 + else + call rename(s:me, s:me . '.old') + call rename(new, s:me) + unlet g:loaded_plug + echo 'vim-plug has been upgraded' + return 1 + endif + finally + silent! call s:rm_rf(tmp) + endtry +endfunction + +function! s:upgrade_specs() + for spec in values(g:plugs) + let spec.frozen = get(spec, 'frozen', 0) + endfor +endfunction + +function! s:status() + call s:prepare() + call append(0, 'Checking plugins') + call append(1, '') + + let ecnt = 0 + let unloaded = 0 + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + let is_dir = isdirectory(spec.dir) + if has_key(spec, 'uri') + if is_dir + let [err, _] = s:git_validate(spec, 1) + let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if is_dir + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif + endif + let cnt += 1 + let ecnt += !valid + " `s:loaded` entry can be missing if PlugUpgraded + if is_dir && get(s:loaded, name, -1) == 0 + let unloaded = 1 + let msg .= ' (not loaded)' + endif + call s:progress_bar(2, repeat('=', cnt), total) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) + normal! 2G + redraw + endfor + call setline(1, 'Finished. '.ecnt.' error(s).') + normal! gg + setlocal nomodifiable + if unloaded + echo "Press 'L' on each line to load plugin, or 'U' to update" + nnoremap 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 + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap 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 branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap (plug-preview) :silent! call preview_commit() + if empty(maparg("\", 'n')) + nmap (plug-preview) + endif + if empty(maparg('o', 'n')) + nmap o (plug-preview) + endif + endif + if cnts[0] + nnoremap X :call revert() + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = s:git_revision(g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@`-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand(':p')) + set shellslash +else + let s:me = resolve(expand(':p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + if has('win32') + \ && &shellslash + \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$') + return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') + endif + if !has('nvim') + \ && (has('win32') || has('win32unix')) + \ && !has('multi_byte') + return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(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! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('plug#end() called without calling plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') + let s:loaded[name] = 1 + continue + endif + + if has_key(plug, 'on') + let s:triggers[name] = { 'map': [], 'cmd': [] } + for cmd in s:to_a(plug.on) + if cmd =~? '^.\+' + 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 + +function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) +endfunction + +if s:is_win + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction + + " Copied from fzf + function! s:wrap_cmds(cmds) + let cmds = [ + \ '@echo off', + \ 'setlocal enabledelayedexpansion'] + \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) + \ + ['endlocal'] + if has('iconv') + if !exists('s:codepage') + let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) + endif + return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) + endif + return map(cmds, 'v:val."\r"') + endfunction + + function! s:batchfile(cmd) + let batchfile = s:plug_tempname().'.bat' + call writefile(s:wrap_cmds(a:cmd), batchfile) + let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) + if &shell =~# 'powershell\(\.exe\)\?$' + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) + endif +endfunction + +function! s:dobufread(names) + for name in a:names + let path = s:rtp(g:plugs[name]) + for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] + if len(finddir(dir, path)) + if exists('#BufRead') + doautocmd BufRead + endif + return + endif + endfor + endfor +endfunction + +function! plug#load(...) + if a:0 == 0 + return s:err('Argument missing: plugin name(s) required') + endif + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 + let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') + if !empty(unknowns) + let s = len(unknowns) > 1 ? 's' : '' + return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) + end + let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') + if !empty(unloaded) + for name in unloaded + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + call s:dobufread(unloaded) + return 1 + end + return 0 +endfunction + +function! s:remove_triggers(name) + if !has_key(s:triggers, a:name) + return + endif + for cmd in s:triggers[a:name].cmd + execute 'silent! delc' cmd + endfor + for map in s:triggers[a:name].map + execute 'silent! unmap' map + execute 'silent! iunmap' map + endfor + call remove(s:triggers, a:name) +endfunction + +function! s:lod(names, types, ...) + for name in a:names + call s:remove_triggers(name) + let s:loaded[name] = 1 + endfor + call s:reorg_rtp() + + for name in a:names + let rtp = s:rtp(g:plugs[name]) + for dir in a:types + call s:source(rtp, dir.'/**/*.vim') + endfor + if a:0 + if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) + execute 'runtime' a:1 + endif + call s:source(rtp, a:2) + endif + call s:doautocmd('User', name) + endfor +endfunction + +function! s:lod_ft(pat, names) + let syn = 'syntax/'.a:pat.'.vim' + call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) + execute 'autocmd! PlugLOD FileType' a:pat + call s:doautocmd('filetypeplugin', 'FileType') + call s:doautocmd('filetypeindent', 'FileType') +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) +endfunction + +function! s:lod_map(map, names, with_prefix, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + + if a:with_prefix + let prefix = v:count ? v:count : '' + let prefix .= '"'.v:register.a:prefix + if mode(1) == 'no' + if v:operator == 'c' + let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(repo . ' ' . v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' + if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif + let opts.tag = a:arg + elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-\{1}\ / + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap 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(s:plug_getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap q :call close_pane() + 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 + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + else + bd + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win + set shell=sh + endif + if a:swap + if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' + set shellredir=>%s\ 2>&1 + endif + endif + return prev +endfunction + +function! s:bang(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') + execute "normal! :execute g:_plug_bang\\" + finally + unlet g:_plug_bang + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + call s:load_plugin(spec) + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:git_revision(a:spec.dir) + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' + let output = s:system( + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = [] + if get(g:, 'plug_shallow', 1) + call extend(s:clone_opt, ['--depth', '1']) + if s:git_version_requirement(1, 7, 10) + call add(s:clone_opt, '--no-single-branch') + endif + endif + + if has('win32unix') || has('wsl') + call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = s:git_origin_branch(spec) + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return (a:event == 'stdout' || a:event == 'stderr') ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + + if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = a:cmd + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = s:plug_call('jobstart', argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) + if has_key(a:opts, 'dir') + let cmd = s:with_cd(cmd, a:opts.dir, 0) + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'err_mode': 'raw', + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + let max = line('$') + for i in range(4, max > 4 ? max : 4) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, max > 5 ? max : 5) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = empty(globpath(spec.dir, '.git', 1)) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, cmd, { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + let cmd = ['git', 'clone'] + if !has_tag + call extend(cmd, s:clone_opt) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt').join(' ') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') +endfunction + +function! s:shellesc_ps1(arg) + return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" +endfunction + +function! s:shellesc_sh(arg) + return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" +endfunction + +" Escape the shell argument based on the shell. +" Vim and Neovim's shellescape() are insufficient. +" 1. shellslash determines whether to use single/double quotes. +" Double-quote escaping is fragile for cmd.exe. +" 2. It does not work for powershell. +" 3. It does not work for *sh shells if the command is executed +" via cmd.exe (ie. cmd.exe /c sh -c command command_args) +" 4. It does not support batchfile syntax. +" +" Accepts an optional dictionary with the following keys: +" - shell: same as Vim/Neovim 'shell' option. +" If unset, fallback to 'cmd.exe' on Windows or 'sh'. +" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. +function! plug#shellescape(arg, ...) + if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' + return a:arg + endif + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\(\.exe\)\?$' + return s:shellesc_cmd(a:arg, script) + elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$' + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) +endfunction + +function! s:system(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + if type(a:cmd) == s:TYPE.list + " Neovim's system() supports list argument to bypass the shell + " but it cannot set the working directory for the command. + " Assume that the command does not rely on the shell. + if has('nvim') && a:0 == 0 + return system(a:cmd) + endif + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) + if &shell =~# 'powershell\(\.exe\)\?$' + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd + endif + if a:0 > 0 + let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) + endif + if s:is_win && type(a:cmd) != s:TYPE.list + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] + let remote = result[-1] + if empty(remote) + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let sha = s:git_revision(a:spec.dir) + if empty(sha) + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let current_branch = result[0] + " Check tag + let origin_branch = s:git_origin_branch(a:spec) + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif origin_branch !=# current_branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ current_branch, origin_branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ origin_branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap 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 + let err_count = 0 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + let err = s:rm_rf(line[2:]) + setlocal modifiable + if empty(err) + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + else + delete _ + call append(l1 - 1, s:format_message('x', line[1:], err)) + let l2 += len(s:lines(err)) + let err_count += 1 + endif + let msg = printf('Removed %d directories.', s:clean_count) + if err_count > 0 + let msg .= printf(' Failed to remove %d directories.', err_count) + endif + call setline(4, msg) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) + if v:shell_error + return s:err('Error upgrading vim-plug: '. out) + endif + + if readfile(s:me) ==# readfile(new) + echo 'vim-plug is already up-to-date' + return 0 + else + call rename(s:me, s:me . '.old') + call rename(new, s:me) + unlet g:loaded_plug + echo 'vim-plug has been upgraded' + return 1 + endif + finally + silent! call s:rm_rf(tmp) + endtry +endfunction + +function! s:upgrade_specs() + for spec in values(g:plugs) + let spec.frozen = get(spec, 'frozen', 0) + endfor +endfunction + +function! s:status() + call s:prepare() + call append(0, 'Checking plugins') + call append(1, '') + + let ecnt = 0 + let unloaded = 0 + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + let is_dir = isdirectory(spec.dir) + if has_key(spec, 'uri') + if is_dir + let [err, _] = s:git_validate(spec, 1) + let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if is_dir + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif + endif + let cnt += 1 + let ecnt += !valid + " `s:loaded` entry can be missing if PlugUpgraded + if is_dir && get(s:loaded, name, -1) == 0 + let unloaded = 1 + let msg .= ' (not loaded)' + endif + call s:progress_bar(2, repeat('=', cnt), total) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) + normal! 2G + redraw + endfor + call setline(1, 'Finished. '.ecnt.' error(s).') + normal! gg + setlocal nomodifiable + if unloaded + echo "Press 'L' on each line to load plugin, or 'U' to update" + nnoremap 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 + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap 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 branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap (plug-preview) :silent! call preview_commit() + if empty(maparg("\", 'n')) + nmap (plug-preview) + endif + if empty(maparg('o', 'n')) + nmap o (plug-preview) + endif + endif + if cnts[0] + nnoremap X :call revert() + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = s:git_revision(g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@= 0.1.5 +if (has("termguicolors")) + set termguicolors +endif + +" Theme +colorscheme dracula " OceanicNext +set background=dark +"""""""""""" + +" allow backspacing over everything in insert mode +set backspace=indent,eol,start + +if has("vms") + set nobackup " do not keep a backup file, use versions instead +else + set backup " keep a backup file +endif + +set undodir=~/.config/nvim/undodir +set undofile +set undolevels=100 +set undoreload=1000 + +set backupdir=~/.config/nvim/backup +set directory=~/.config/nvim/backup + +set ruler " show the cursor position all the time +set cursorline + +set showcmd " display incomplete commands + +if has('mouse') + set mouse=a +endif + +if has("autocmd") + + augroup vimrcEx + au! + + " For all text files set 'textwidth' to 78 characters. + autocmd FileType text setlocal textwidth=108 + + " Trim whitespace onsave + autocmd BufWritePre * %s/\s\+$//e + + " When editing a file, always jump to the last known cursor position. + " Don't do it when the position is invalid or when inside an event handler + " (happens when dropping a file on gvim). + " Also don't do it when the mark is in the first line, that is the default + " position when opening a file. + autocmd BufReadPost * + \ if line("'\"") > 1 && line("'\"") <= line("$") | + \ exe "normal! g`\"" | + \ endif + + augroup END + +endif " has("autocmd") + +" tab stuff +set tabstop=2 +set softtabstop=2 +set expandtab +set smarttab +set shiftwidth=2 +set autoindent +set smartindent + +set complete-=i + +set nrformats-=octal + +set ttimeout +set ttimeoutlen=100 + +" detect .md as markdown instead of modula-2 +autocmd BufNewFile,BufReadPost *.md set filetype=markdown + +" Unix as standard file type +set ffs=unix,dos,mac + +" Always utf8 +set termencoding=utf-8 +set encoding=utf-8 +set fileencoding=utf-8 + +set so=5 " scroll lines above/below cursor +set sidescrolloff=5 +set lazyredraw + +set magic " for regular expressions + +if &listchars ==# 'eol:$' + set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ +endif + +if has('path_extra') + setglobal tags-=./tags tags^=./tags; +endif + +set autoread + +if &history < 1000 + set history=1000 +endif +if &tabpagemax < 50 + set tabpagemax=50 +endif +if !empty(&viminfo) + set viminfo^=! +endif +set sessionoptions-=options + +" buffer settings +set hid " buffer becomes hidden when abandoned + +" stop highlighting of underscores in markdown files +autocmd BufNewFile,BufRead,BufEnter *.md,*.markdown :syntax match markdownIgnore "_" + +" clipboard " salam:x +if (executable('pbcopy') || executable('xclip') || executable('xsel')) && has('clipboard') + set clipboard=unnamed +endif +" set clipboard=unnamedplus " salam diff --git a/vim/config/init.vimrc b/vim/config/init.vimrc new file mode 100644 index 0000000..3cbb2be --- /dev/null +++ b/vim/config/init.vimrc @@ -0,0 +1,81 @@ +call plug#begin('~/.config/nvim/plugged') + +" for sort these lines +" :'<,'>sort + +" general +Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } " dark powered neo-completion +Plug 'SirVer/ultisnips' " completion ~~ UltiSnips is the ultimate solution for snippets in Vim +Plug 'benekastah/neomake' " asynchronously runs programs job-control functionality +Plug 'christoomey/vim-tmux-navigator' " navigate from vim to tmux windows +Plug 'ervandew/supertab' " super tab +Plug 'haya14busa/incsearch.vim' " search example: /salam +Plug 'honza/vim-snippets' " vim snippets +Plug 'jaawerth/neomake-local-eslint-first' " for eslint local +Plug 'kien/ctrlp.vim' " quick look & open file +Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } " nerd tree +Plug 'sheerun/vim-polyglot' " A collection of language packs for Vim. +Plug 'terryma/vim-multiple-cursors' " Ctrl n multi cursor + +" editing +Plug 'Raimondi/delimitMate' " automatic closing of quotes, parenthesis, brackets, etc. +Plug 'airblade/vim-gitgutter' " shows a git diff in the 'gutter' +Plug 'junegunn/vim-easy-align' " indent multi text ( easy align : so many feature ), eg: `gaip* ` `v2j*:` +Plug 'mbbill/undotree' " undo tree +Plug 'nathanaelkane/vim-indent-guides' " `,ig` to toggle +Plug 'tpope/vim-commentary' " for multiline became to comment lines +Plug 'tpope/vim-repeat' " Repeat.vim remaps . in a way that plugins can tap into it +Plug 'tpope/vim-speeddating' " a Ctrl-a/Ctrl-x command to increment / decrement a line of Date +Plug 'tpope/vim-surround' " ysiw' | ds{ds) provides mappings to easily delete, change and add such surroundings in pairs +Plug 'tpope/vim-unimpaired' " [a cursor to previous a, ]x cursor to next x +Plug 'vim-scripts/camelcasemotion' " when w,e,b help cursor stay at camelCaseWord + +" eye candy +Plug 'lilydjwg/colorizer', { 'on': 'ColorToggle' } " #aaa to Colorize +Plug 'myusuf3/numbers.vim' " intelligently toggling line numbers +Plug 'vim-airline/vim-airline' " Lean & mean status/tabline for vim that's light as air +Plug 'vim-airline/vim-airline-themes' " airline theme... + +" colorschemes +Plug 'dracula/vim', { 'as': 'dracula' } " favorite theme +Plug 'ryanoasis/vim-devicons' " look at beauty icons + +" text objects +Plug 'glts/vim-textobj-comment' " provides text objects for comments , eg. SHIFT + \" +Plug 'kana/vim-textobj-fold' " provides text objects for fold , eg. yaz to yank arround fold +Plug 'kana/vim-textobj-function' " Text objects for functions, build error ??! +Plug 'kana/vim-textobj-indent' " Text objects for indented blocks of lines, eg. y- yank of blocks of lines +Plug 'kana/vim-textobj-user' " create your own text objects without pain, eg. call textobj#user#plugin('datetime', { ... +Plug 'wellle/targets.vim' " ci' to (cut in single quote), also support (I l L a A n N) for (In Last last in .. next Next) + +" javascript +Plug 'isRuslan/vim-es6' " Write JavaScript ES6 easily with vim. (You need SnipMate or UltiSnips installed. ??) +Plug 'leafgarland/typescript-vim' " just for typescript +Plug 'mhartington/vim-angular2-snippets' " for TS and ng2 (version 4.o.o >=) +Plug 'moll/vim-node' " gf to jump to source and module files, :Nedit {module_name} to edit the main file of a module +Plug 'mxw/vim-jsx', { 'for': ['javascript', 'javascript.jsx'] } " JSX (React) syntax highlighting and indenting for vim +Plug 'othree/javascript-libraries-syntax.vim' " Supports JavaScript libraries . Should work well with other (ng, react, vue, coffeScript, typeScript...) +Plug 'othree/yajs.vim' " Another JavaScript Syntax file for Vim. Key differences +Plug 'pangloss/vim-javascript', { 'for': ['javascript', 'javascript.jsx', 'vue', 'vue.html.javascript.css'] } " rovides syntax highlighting and improved indentation +Plug 'posva/vim-vue' + +" golang +" Require: go get -u github.com/nsf/gocode +Plug 'fatih/vim-go' , { 'do': ':GoInstallBinaries' } " GoUpdateBinaries will take a long time + +" python +Plug 'python-mode/python-mode', { 'for': 'python', 'branch': 'develop' } + +" html +Plug 'mattn/emmet-vim' " provides support for expanding abbreviations similar to emmet +Plug 'othree/html5.vim' " HTML5 + inline SVG omnicomplete function, indent and syntax for Vim. Based on the default htmlcomplete.vim, This plugin contributes to vim-polyglot language pack. + +" css +Plug 'hail2u/vim-css3-syntax' " CSS3 syntax (and syntax defined in some foreign specifications) support for Vim’s built-in +Plug 'othree/csscomplete.vim' " Update the bult-in CSS complete function to latest CSS standard. +Plug 'wavded/vim-stylus' " for stylus + +" json +Plug 'elzr/vim-json' " distinct highlighting of keywords vs values, JSON-specific (non-JS) warnings, quote concealing. Pathogen-friendly, support in polyglot + +call plug#end() diff --git a/vim/config/keys.vimrc b/vim/config/keys.vimrc new file mode 100644 index 0000000..696064d --- /dev/null +++ b/vim/config/keys.vimrc @@ -0,0 +1,136 @@ +" map Leader +let mapleader = "," + +" keep backward f search, remapping it to ,; +nnoremap ; , + +" buffer keys +nnoremap bb :b# +nnoremap n :bn +nnoremap q :bp +nnoremap bf :bf +nnoremap bl :bl +nnoremap bw :w:bd +nnoremap d :bd! +" new buffer/tab +nnoremap e :enew + +" window keys +nnoremap w< < +nnoremap w> > +nnoremap w- - +nnoremap w+ + +nnoremap s :split +nnoremap v :vsplit +nnoremap wx :close + +" for commentary +nnoremap " :Commentary +vnoremap " :Commentary +" :7,17Commentary + +" multiline moving +" ∆ +" ˚ +nnoremap ∆ :m .+1== +nnoremap ˚ :m .-2== +vnoremap ∆ :m '>+1gv=gv +vnoremap ˚ :m '<-2gv=gv +inoremap ∆ :m .+1==gi +inoremap ˚ :m .-2==gi + +" command mode maps +" better command-line window scrolling with & +" cnoremap +" cnoremap + +" %% to expand active buffer location on cmdline +cnoremap %% getcmdtype() == ':' ? expand('%:h').'/' : '%%' + +" CtrlP keys +nnoremap pp :CtrlP +nnoremap pm :CtrlPMRUFiles +nnoremap pb :CtrlPBuffer + +" Function keys +nnoremap :let _s=@/:%s/\s\+$//e:let @/=_s:nohl +nnoremap :NERDTreeToggle +nnoremap :source $HOME/.config/nvim/init.vim +nnoremap :set hlsearch! +nnoremap :UndotreeToggle +" nnoremap :Geeknote +" indent whole file according to syntax rules +" noremap gg=G + +" CTRL-U in insert mode deletes a lot. Use CTRL-G u to first break undo, +" so that you can undo CTRL-U after inserting a line break. +inoremap u +" Don't use Ex mode, use Q for formatting +map Q gq + +" relative line numbers +nnoremap 3 :NumbersToggle + +" snippets +let g:UltiSnipsExpandTrigger="" + +" remap number increment to C-s (C-a is already in use by tmux) +nmap + +" Word count selection +vnoremap w :w !wc -w + +" vim paste mode toggle (for fixing indentation issues when pasting text) +nnoremap :set invpaste paste? +set pastetoggle= +set showmode + +" override read-only permissions +cmap w!! %!sudo tee > /dev/null % + +" allow ,, for vimsneak +nmap , SneakPrevious + +" camelCase motion settings +map w CamelCaseMotion_w +map b CamelCaseMotion_b +map e CamelCaseMotion_e +sunmap w +sunmap b +sunmap e + +" start interactive EasyAlign in visual mode (e.g. vip) +vmap (EasyAlign) + +" start interactive EasyAlign for a motion/text object (e.g. gaip) +nmap ga (EasyAlign) + +" neomake +nmap o :lopen +nmap c :lclose +nmap , :ll +nmap n :lnext +nmap p :lprev + +" folding +nmap f zf% +" Folding {{{ +set foldenable " enable folding +set foldlevelstart=2 " start folding then we are 10 blocks deep +set foldnestmax=5 " 10 nested fold max +set foldmethod=indent " fold based on indent level +"}}} + +" deoplete tab-complete +inoremap pumvisible() ? "\" : deoplete#mappings#manual_complete() +" , for regular tab +inoremap + +" tern +autocmd FileType javascript nnoremap gb :TernDef + +" colorizer +nmap tc :ColorToggle + +" for vnoremap selection search +vnoremap / y/" diff --git a/vim/config/line.vimrc b/vim/config/line.vimrc new file mode 100644 index 0000000..040f536 --- /dev/null +++ b/vim/config/line.vimrc @@ -0,0 +1,53 @@ +" All status line configuration goes here + +set cmdheight=1 +set display+=lastline + +" general config +set laststatus=2 " always show status line +set showtabline=2 " always show tabline +set noshowmode " hide default mode text (e.g. INSERT) as airline already displays it + +" airline config +let g:airline_powerline_fonts=1 +let g:airline#extensions#tabline#enabled=1 " buffers at the top as tabs +let g:airline#extensions#tabline#show_tabs=0 +let g:airline#extensions#tabline#show_tab_type=1 +let g:airline#extensions#tmuxline#enabled=0 +let g:airline_theme = 'lucius' +if !exists('g:airline_symbols') + let g:airline_symbols = {} +endif + +let g:airline_symbols.linenr = '' +let g:airline_symbols.paste = 'ρ' +let g:airline_symbols.readonly = '' + +let g:airline#extensions#quickfix#quickfix_text = 'QF' +let g:airline#extensions#quickfix#location_text = 'LL' + +" disable unused extensions (performance) +let g:airline#extensions#bufferline#enabled = 0 +let g:airline#extensions#capslock#enabled = 0 +let g:airline#extensions#csv#enabled = 0 +let g:airline#extensions#ctrlspace#enabled = 0 +let g:airline#extensions#eclim#enabled = 0 +let g:airline#extensions#hunks#enabled = 0 +let g:airline#extensions#nrrwrgn#enabled = 0 +let g:airline#extensions#promptline#enabled = 0 +let g:airline#extensions#syntastic#enabled = 0 +let g:airline#extensions#taboo#enabled = 0 +let g:airline#extensions#tagbar#enabled = 0 +let g:airline#extensions#virtualenv#enabled = 0 +let g:airline#extensions#whitespace#enabled = 0 + +" tmuxline config +let g:tmuxline_preset = { + \ 'a': '#S', + \ 'b': '#F', + \ 'c': '#W', + \ 'win': ['#I', '#W'], + \ 'cwin': ['#I', '#W'], + \ 'x': '#h', + \ 'y': '%b %d', + \ 'z': '%R'} diff --git a/vim/config/plugins.vimrc b/vim/config/plugins.vimrc new file mode 100644 index 0000000..a07268a --- /dev/null +++ b/vim/config/plugins.vimrc @@ -0,0 +1,127 @@ +filetype plugin indent on + +" deoplete + +let g:deoplete#enable_at_startup = 1 +let g:deoplete#disable_auto_complete = 1 +autocmd InsertLeave,CompleteDone * if pumvisible() == 0 | pclose | endif +let g:deoplete#omni_input_patterns = {} +" if !exists('g:deoplete#omni#input_patterns') +" let g:deoplete#omni#input_patterns = {} +" endif + +if system('uname -s') == "Darwin\n" + " OSX + let g:python3_host_prog = '/opt/homebrew/Caskroom/miniforge/base/bin/python3' + " /usr/local/bin/python3 +else + " Linux + let g:python3_host_prog = '/usr/bin/python3' +endif + +" Linux & (new) OSX +" let g:python3_host_prog = '/usr/bin/python3' + +" neomake config +autocmd! BufWritePost * Neomake +" autocmd BufLeave * QFix + +let g:neomake_warning_sign = { + \ 'text': 'W', + \ 'texthl': 'WarningMsg', + \ } + +let g:neomake_error_sign = { + \ 'text': 'E', + \ 'texthl': 'ErrorMsg', + \ } + +let g:neomake_list_height = 3 +let g:neomake_open_list = 2 +let g:neomake_verbose = 2 + +let b:neomake_javascript_eslint_exe = nrun#Which('eslint') " for find local eslint +let g:neomake_javascript_enabled_makers = ['eslint'] +if exists(':Neomake') + " Check for lint errors on open & write + autocmd BufRead,BufWritePost,BufEnter *.js,*.jsx,*.vue silent! Neomake standard|redraw +endif + +" make background transparent +hi Normal ctermbg=NONE +hi EndOfBuffer ctermbg=NONE +hi LineNr ctermbg=234 + +" CtrlP +let g:ctrlp_prompt_mappings={'PrtClearCache()':['']} +let g:ctrlp_prompt_mappings={'PrtdeleteEnt()':['']} +let g:ctrlp_match_window='bottom,order:btt,min:2,max:15' +set wildmenu " enhanced autocomplete +set wildignore+=*/tmp/*,*/node_modules/*,*/.git/*,*.so,*.swp,*.zip,*node_modules*,*.jpg,*.png,*.svg,*.ttf,*.woff,*.woff3,*.eot +",*public/css/*,*public/js* +" let g:ctrlp_user_command = 'find %s -type f' + +" delimitMate options +let delimitMate_expand_cr=1 + +" enable matchit (for matching tags with %) +runtime macros/matchit.vim + +" vim-sneak settings +hi SneakPluginTarget ctermfg=black ctermbg=181818 + +" javascript libraries syntax +let g:used_javascript_libs = 'vue' " underscore,react,chai, more to see -> /othree/javascript-libraries-syntax.vim + +" tern +if exists('g:plugs["tern_for_vim"]') + let g:tern_show_argument_hints = 'on_hold' + let g:tern_show_signature_in_pum = 1 + + autocmd FileType javascript setlocal omnifunc=tern#Complete +endif + +" disable colorizer at startup +let g:colorizer_startup = 0 +let g:colorizer_nomap = 1 + +" " ale (eslint) +" set nocompatible +" filetype off +" let &runtimepath.=',~/.config/nvim/plugged/ale' +" filetype plugin on +" silent! helptags ALL + +" vim-devicons +set guifont=Droid\ Sans\ Mono\ for\ Powerline\ Plus\ Nerd\ File\ Types:h11 +let g:airline_powerline_fonts = 1 + +" NERDTree +let g:NERDTreeWinPos = 'right' + +" vim-json +hi! def link jsonKeyword Identifier +let g:vim_json_syntax_conceal = 1 + +" javascript +autocmd BufRead,BufNewFile *.js setlocal filetype=javascript + +" vue +autocmd BufRead,BufNewFile *.vue setlocal filetype=vue.html.javascript.css +" let g:vue_disable_pre_processors = 1 + +" For php tab space +autocmd Filetype php setlocal ts=4 sw=4 sts=0 expandtab +autocmd Filetype blade setlocal ts=2 sw=2 sts=0 expandtab + +" vim-go +let g:deoplete#sources#go#gocode_binary = $GOPATH.'/bin/gocode' +let g:deoplete#sources#go#sort_class = ['package', 'func', 'type', 'var', 'const'] +autocmd Filetype go setlocal ts=2 sw=2 sts=0 expandtab + +" Python +" pythonmod +let g:pymode_lint = 1 +let g:pymode_python = 'python3' +" fold +autocmd BufReadPost,BufEnter *.py normal zM diff --git a/vim/init.vim b/vim/init.vim new file mode 100644 index 0000000..55a1858 --- /dev/null +++ b/vim/init.vim @@ -0,0 +1,5 @@ +source $HOME/.config/nvim/config/init.vimrc +source $HOME/.config/nvim/config/general.vimrc +source $HOME/.config/nvim/config/plugins.vimrc +source $HOME/.config/nvim/config/keys.vimrc +source $HOME/.config/nvim/config/line.vimrc From 7f1de75632fcabb8bdcf671476323ceb75373dfc Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Sun, 31 Jul 2022 06:31:00 -0700 Subject: [PATCH 13/25] nvim --- .inputrc | 2 +- {vim => nvim}/.vimrc | 0 {vim => nvim}/README.md | 0 {vim => nvim}/autoload/.gitignore | 0 {vim => nvim}/autoload/plug.vim | 0 {vim => nvim}/autoload/plug.vim.old | 0 {vim => nvim}/init.vim | 0 nvim/nvim | 1 + vim/config/.gitignore | 1 - vim/config/general.vimrc | 138 ---------------------------- vim/config/init.vimrc | 81 ---------------- vim/config/keys.vimrc | 136 --------------------------- vim/config/line.vimrc | 53 ----------- vim/config/plugins.vimrc | 127 ------------------------- zsh/.antigenrc | 38 ++++++++ zsh/.zprofile | 7 ++ zsh/.zshenv | 3 + 17 files changed, 50 insertions(+), 537 deletions(-) rename {vim => nvim}/.vimrc (100%) rename {vim => nvim}/README.md (100%) rename {vim => nvim}/autoload/.gitignore (100%) rename {vim => nvim}/autoload/plug.vim (100%) rename {vim => nvim}/autoload/plug.vim.old (100%) rename {vim => nvim}/init.vim (100%) create mode 160000 nvim/nvim delete mode 100644 vim/config/.gitignore delete mode 100644 vim/config/general.vimrc delete mode 100644 vim/config/init.vimrc delete mode 100644 vim/config/keys.vimrc delete mode 100644 vim/config/line.vimrc delete mode 100644 vim/config/plugins.vimrc create mode 100644 zsh/.antigenrc create mode 100644 zsh/.zprofile create mode 100644 zsh/.zshenv diff --git a/.inputrc b/.inputrc index d77e19e..6ee7ac3 100644 --- a/.inputrc +++ b/.inputrc @@ -1,4 +1,4 @@ -set editing-mode = vi +set editing-mode vi set blink-matching-paren on set colored-stats on set completion-ignore-case on diff --git a/vim/.vimrc b/nvim/.vimrc similarity index 100% rename from vim/.vimrc rename to nvim/.vimrc diff --git a/vim/README.md b/nvim/README.md similarity index 100% rename from vim/README.md rename to nvim/README.md diff --git a/vim/autoload/.gitignore b/nvim/autoload/.gitignore similarity index 100% rename from vim/autoload/.gitignore rename to nvim/autoload/.gitignore diff --git a/vim/autoload/plug.vim b/nvim/autoload/plug.vim similarity index 100% rename from vim/autoload/plug.vim rename to nvim/autoload/plug.vim diff --git a/vim/autoload/plug.vim.old b/nvim/autoload/plug.vim.old similarity index 100% rename from vim/autoload/plug.vim.old rename to nvim/autoload/plug.vim.old diff --git a/vim/init.vim b/nvim/init.vim similarity index 100% rename from vim/init.vim rename to nvim/init.vim diff --git a/nvim/nvim b/nvim/nvim new file mode 160000 index 0000000..7ee31c0 --- /dev/null +++ b/nvim/nvim @@ -0,0 +1 @@ +Subproject commit 7ee31c09e13abc0956d341a12357bfa70323d41d diff --git a/vim/config/.gitignore b/vim/config/.gitignore deleted file mode 100644 index f9be8df..0000000 --- a/vim/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!* diff --git a/vim/config/general.vimrc b/vim/config/general.vimrc deleted file mode 100644 index 2f52956..0000000 --- a/vim/config/general.vimrc +++ /dev/null @@ -1,138 +0,0 @@ -syntax on -syntax enable - -set hlsearch - -set number - - -"""""""""""""" -" For Neovim 0.1.3 and 0.1.4 -let $NVIM_TUI_ENABLE_TRUE_COLOR=1 - -" Or if you have Neovim >= 0.1.5 -if (has("termguicolors")) - set termguicolors -endif - -" Theme -colorscheme dracula " OceanicNext -set background=dark -"""""""""""" - -" allow backspacing over everything in insert mode -set backspace=indent,eol,start - -if has("vms") - set nobackup " do not keep a backup file, use versions instead -else - set backup " keep a backup file -endif - -set undodir=~/.config/nvim/undodir -set undofile -set undolevels=100 -set undoreload=1000 - -set backupdir=~/.config/nvim/backup -set directory=~/.config/nvim/backup - -set ruler " show the cursor position all the time -set cursorline - -set showcmd " display incomplete commands - -if has('mouse') - set mouse=a -endif - -if has("autocmd") - - augroup vimrcEx - au! - - " For all text files set 'textwidth' to 78 characters. - autocmd FileType text setlocal textwidth=108 - - " Trim whitespace onsave - autocmd BufWritePre * %s/\s\+$//e - - " When editing a file, always jump to the last known cursor position. - " Don't do it when the position is invalid or when inside an event handler - " (happens when dropping a file on gvim). - " Also don't do it when the mark is in the first line, that is the default - " position when opening a file. - autocmd BufReadPost * - \ if line("'\"") > 1 && line("'\"") <= line("$") | - \ exe "normal! g`\"" | - \ endif - - augroup END - -endif " has("autocmd") - -" tab stuff -set tabstop=2 -set softtabstop=2 -set expandtab -set smarttab -set shiftwidth=2 -set autoindent -set smartindent - -set complete-=i - -set nrformats-=octal - -set ttimeout -set ttimeoutlen=100 - -" detect .md as markdown instead of modula-2 -autocmd BufNewFile,BufReadPost *.md set filetype=markdown - -" Unix as standard file type -set ffs=unix,dos,mac - -" Always utf8 -set termencoding=utf-8 -set encoding=utf-8 -set fileencoding=utf-8 - -set so=5 " scroll lines above/below cursor -set sidescrolloff=5 -set lazyredraw - -set magic " for regular expressions - -if &listchars ==# 'eol:$' - set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ -endif - -if has('path_extra') - setglobal tags-=./tags tags^=./tags; -endif - -set autoread - -if &history < 1000 - set history=1000 -endif -if &tabpagemax < 50 - set tabpagemax=50 -endif -if !empty(&viminfo) - set viminfo^=! -endif -set sessionoptions-=options - -" buffer settings -set hid " buffer becomes hidden when abandoned - -" stop highlighting of underscores in markdown files -autocmd BufNewFile,BufRead,BufEnter *.md,*.markdown :syntax match markdownIgnore "_" - -" clipboard " salam:x -if (executable('pbcopy') || executable('xclip') || executable('xsel')) && has('clipboard') - set clipboard=unnamed -endif -" set clipboard=unnamedplus " salam diff --git a/vim/config/init.vimrc b/vim/config/init.vimrc deleted file mode 100644 index 3cbb2be..0000000 --- a/vim/config/init.vimrc +++ /dev/null @@ -1,81 +0,0 @@ -call plug#begin('~/.config/nvim/plugged') - -" for sort these lines -" :'<,'>sort - -" general -Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } " dark powered neo-completion -Plug 'SirVer/ultisnips' " completion ~~ UltiSnips is the ultimate solution for snippets in Vim -Plug 'benekastah/neomake' " asynchronously runs programs job-control functionality -Plug 'christoomey/vim-tmux-navigator' " navigate from vim to tmux windows -Plug 'ervandew/supertab' " super tab -Plug 'haya14busa/incsearch.vim' " search example: /salam -Plug 'honza/vim-snippets' " vim snippets -Plug 'jaawerth/neomake-local-eslint-first' " for eslint local -Plug 'kien/ctrlp.vim' " quick look & open file -Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } " nerd tree -Plug 'sheerun/vim-polyglot' " A collection of language packs for Vim. -Plug 'terryma/vim-multiple-cursors' " Ctrl n multi cursor - -" editing -Plug 'Raimondi/delimitMate' " automatic closing of quotes, parenthesis, brackets, etc. -Plug 'airblade/vim-gitgutter' " shows a git diff in the 'gutter' -Plug 'junegunn/vim-easy-align' " indent multi text ( easy align : so many feature ), eg: `gaip* ` `v2j*:` -Plug 'mbbill/undotree' " undo tree -Plug 'nathanaelkane/vim-indent-guides' " `,ig` to toggle -Plug 'tpope/vim-commentary' " for multiline became to comment lines -Plug 'tpope/vim-repeat' " Repeat.vim remaps . in a way that plugins can tap into it -Plug 'tpope/vim-speeddating' " a Ctrl-a/Ctrl-x command to increment / decrement a line of Date -Plug 'tpope/vim-surround' " ysiw' | ds{ds) provides mappings to easily delete, change and add such surroundings in pairs -Plug 'tpope/vim-unimpaired' " [a cursor to previous a, ]x cursor to next x -Plug 'vim-scripts/camelcasemotion' " when w,e,b help cursor stay at camelCaseWord - -" eye candy -Plug 'lilydjwg/colorizer', { 'on': 'ColorToggle' } " #aaa to Colorize -Plug 'myusuf3/numbers.vim' " intelligently toggling line numbers -Plug 'vim-airline/vim-airline' " Lean & mean status/tabline for vim that's light as air -Plug 'vim-airline/vim-airline-themes' " airline theme... - -" colorschemes -Plug 'dracula/vim', { 'as': 'dracula' } " favorite theme -Plug 'ryanoasis/vim-devicons' " look at beauty icons - -" text objects -Plug 'glts/vim-textobj-comment' " provides text objects for comments , eg. SHIFT + \" -Plug 'kana/vim-textobj-fold' " provides text objects for fold , eg. yaz to yank arround fold -Plug 'kana/vim-textobj-function' " Text objects for functions, build error ??! -Plug 'kana/vim-textobj-indent' " Text objects for indented blocks of lines, eg. y- yank of blocks of lines -Plug 'kana/vim-textobj-user' " create your own text objects without pain, eg. call textobj#user#plugin('datetime', { ... -Plug 'wellle/targets.vim' " ci' to (cut in single quote), also support (I l L a A n N) for (In Last last in .. next Next) - -" javascript -Plug 'isRuslan/vim-es6' " Write JavaScript ES6 easily with vim. (You need SnipMate or UltiSnips installed. ??) -Plug 'leafgarland/typescript-vim' " just for typescript -Plug 'mhartington/vim-angular2-snippets' " for TS and ng2 (version 4.o.o >=) -Plug 'moll/vim-node' " gf to jump to source and module files, :Nedit {module_name} to edit the main file of a module -Plug 'mxw/vim-jsx', { 'for': ['javascript', 'javascript.jsx'] } " JSX (React) syntax highlighting and indenting for vim -Plug 'othree/javascript-libraries-syntax.vim' " Supports JavaScript libraries . Should work well with other (ng, react, vue, coffeScript, typeScript...) -Plug 'othree/yajs.vim' " Another JavaScript Syntax file for Vim. Key differences -Plug 'pangloss/vim-javascript', { 'for': ['javascript', 'javascript.jsx', 'vue', 'vue.html.javascript.css'] } " rovides syntax highlighting and improved indentation -Plug 'posva/vim-vue' - -" golang -" Require: go get -u github.com/nsf/gocode -Plug 'fatih/vim-go' , { 'do': ':GoInstallBinaries' } " GoUpdateBinaries will take a long time - -" python -Plug 'python-mode/python-mode', { 'for': 'python', 'branch': 'develop' } - -" html -Plug 'mattn/emmet-vim' " provides support for expanding abbreviations similar to emmet -Plug 'othree/html5.vim' " HTML5 + inline SVG omnicomplete function, indent and syntax for Vim. Based on the default htmlcomplete.vim, This plugin contributes to vim-polyglot language pack. - -" css -Plug 'hail2u/vim-css3-syntax' " CSS3 syntax (and syntax defined in some foreign specifications) support for Vim’s built-in -Plug 'othree/csscomplete.vim' " Update the bult-in CSS complete function to latest CSS standard. -Plug 'wavded/vim-stylus' " for stylus - -" json -Plug 'elzr/vim-json' " distinct highlighting of keywords vs values, JSON-specific (non-JS) warnings, quote concealing. Pathogen-friendly, support in polyglot - -call plug#end() diff --git a/vim/config/keys.vimrc b/vim/config/keys.vimrc deleted file mode 100644 index 696064d..0000000 --- a/vim/config/keys.vimrc +++ /dev/null @@ -1,136 +0,0 @@ -" map Leader -let mapleader = "," - -" keep backward f search, remapping it to ,; -nnoremap ; , - -" buffer keys -nnoremap bb :b# -nnoremap n :bn -nnoremap q :bp -nnoremap bf :bf -nnoremap bl :bl -nnoremap bw :w:bd -nnoremap d :bd! -" new buffer/tab -nnoremap e :enew - -" window keys -nnoremap w< < -nnoremap w> > -nnoremap w- - -nnoremap w+ + -nnoremap s :split -nnoremap v :vsplit -nnoremap wx :close - -" for commentary -nnoremap " :Commentary -vnoremap " :Commentary -" :7,17Commentary - -" multiline moving -" ∆ -" ˚ -nnoremap ∆ :m .+1== -nnoremap ˚ :m .-2== -vnoremap ∆ :m '>+1gv=gv -vnoremap ˚ :m '<-2gv=gv -inoremap ∆ :m .+1==gi -inoremap ˚ :m .-2==gi - -" command mode maps -" better command-line window scrolling with & -" cnoremap -" cnoremap - -" %% to expand active buffer location on cmdline -cnoremap %% getcmdtype() == ':' ? expand('%:h').'/' : '%%' - -" CtrlP keys -nnoremap pp :CtrlP -nnoremap pm :CtrlPMRUFiles -nnoremap pb :CtrlPBuffer - -" Function keys -nnoremap :let _s=@/:%s/\s\+$//e:let @/=_s:nohl -nnoremap :NERDTreeToggle -nnoremap :source $HOME/.config/nvim/init.vim -nnoremap :set hlsearch! -nnoremap :UndotreeToggle -" nnoremap :Geeknote -" indent whole file according to syntax rules -" noremap gg=G - -" CTRL-U in insert mode deletes a lot. Use CTRL-G u to first break undo, -" so that you can undo CTRL-U after inserting a line break. -inoremap u -" Don't use Ex mode, use Q for formatting -map Q gq - -" relative line numbers -nnoremap 3 :NumbersToggle - -" snippets -let g:UltiSnipsExpandTrigger="" - -" remap number increment to C-s (C-a is already in use by tmux) -nmap - -" Word count selection -vnoremap w :w !wc -w - -" vim paste mode toggle (for fixing indentation issues when pasting text) -nnoremap :set invpaste paste? -set pastetoggle= -set showmode - -" override read-only permissions -cmap w!! %!sudo tee > /dev/null % - -" allow ,, for vimsneak -nmap , SneakPrevious - -" camelCase motion settings -map w CamelCaseMotion_w -map b CamelCaseMotion_b -map e CamelCaseMotion_e -sunmap w -sunmap b -sunmap e - -" start interactive EasyAlign in visual mode (e.g. vip) -vmap (EasyAlign) - -" start interactive EasyAlign for a motion/text object (e.g. gaip) -nmap ga (EasyAlign) - -" neomake -nmap o :lopen -nmap c :lclose -nmap , :ll -nmap n :lnext -nmap p :lprev - -" folding -nmap f zf% -" Folding {{{ -set foldenable " enable folding -set foldlevelstart=2 " start folding then we are 10 blocks deep -set foldnestmax=5 " 10 nested fold max -set foldmethod=indent " fold based on indent level -"}}} - -" deoplete tab-complete -inoremap pumvisible() ? "\" : deoplete#mappings#manual_complete() -" , for regular tab -inoremap - -" tern -autocmd FileType javascript nnoremap gb :TernDef - -" colorizer -nmap tc :ColorToggle - -" for vnoremap selection search -vnoremap / y/" diff --git a/vim/config/line.vimrc b/vim/config/line.vimrc deleted file mode 100644 index 040f536..0000000 --- a/vim/config/line.vimrc +++ /dev/null @@ -1,53 +0,0 @@ -" All status line configuration goes here - -set cmdheight=1 -set display+=lastline - -" general config -set laststatus=2 " always show status line -set showtabline=2 " always show tabline -set noshowmode " hide default mode text (e.g. INSERT) as airline already displays it - -" airline config -let g:airline_powerline_fonts=1 -let g:airline#extensions#tabline#enabled=1 " buffers at the top as tabs -let g:airline#extensions#tabline#show_tabs=0 -let g:airline#extensions#tabline#show_tab_type=1 -let g:airline#extensions#tmuxline#enabled=0 -let g:airline_theme = 'lucius' -if !exists('g:airline_symbols') - let g:airline_symbols = {} -endif - -let g:airline_symbols.linenr = '' -let g:airline_symbols.paste = 'ρ' -let g:airline_symbols.readonly = '' - -let g:airline#extensions#quickfix#quickfix_text = 'QF' -let g:airline#extensions#quickfix#location_text = 'LL' - -" disable unused extensions (performance) -let g:airline#extensions#bufferline#enabled = 0 -let g:airline#extensions#capslock#enabled = 0 -let g:airline#extensions#csv#enabled = 0 -let g:airline#extensions#ctrlspace#enabled = 0 -let g:airline#extensions#eclim#enabled = 0 -let g:airline#extensions#hunks#enabled = 0 -let g:airline#extensions#nrrwrgn#enabled = 0 -let g:airline#extensions#promptline#enabled = 0 -let g:airline#extensions#syntastic#enabled = 0 -let g:airline#extensions#taboo#enabled = 0 -let g:airline#extensions#tagbar#enabled = 0 -let g:airline#extensions#virtualenv#enabled = 0 -let g:airline#extensions#whitespace#enabled = 0 - -" tmuxline config -let g:tmuxline_preset = { - \ 'a': '#S', - \ 'b': '#F', - \ 'c': '#W', - \ 'win': ['#I', '#W'], - \ 'cwin': ['#I', '#W'], - \ 'x': '#h', - \ 'y': '%b %d', - \ 'z': '%R'} diff --git a/vim/config/plugins.vimrc b/vim/config/plugins.vimrc deleted file mode 100644 index a07268a..0000000 --- a/vim/config/plugins.vimrc +++ /dev/null @@ -1,127 +0,0 @@ -filetype plugin indent on - -" deoplete - -let g:deoplete#enable_at_startup = 1 -let g:deoplete#disable_auto_complete = 1 -autocmd InsertLeave,CompleteDone * if pumvisible() == 0 | pclose | endif -let g:deoplete#omni_input_patterns = {} -" if !exists('g:deoplete#omni#input_patterns') -" let g:deoplete#omni#input_patterns = {} -" endif - -if system('uname -s') == "Darwin\n" - " OSX - let g:python3_host_prog = '/opt/homebrew/Caskroom/miniforge/base/bin/python3' - " /usr/local/bin/python3 -else - " Linux - let g:python3_host_prog = '/usr/bin/python3' -endif - -" Linux & (new) OSX -" let g:python3_host_prog = '/usr/bin/python3' - -" neomake config -autocmd! BufWritePost * Neomake -" autocmd BufLeave * QFix - -let g:neomake_warning_sign = { - \ 'text': 'W', - \ 'texthl': 'WarningMsg', - \ } - -let g:neomake_error_sign = { - \ 'text': 'E', - \ 'texthl': 'ErrorMsg', - \ } - -let g:neomake_list_height = 3 -let g:neomake_open_list = 2 -let g:neomake_verbose = 2 - -let b:neomake_javascript_eslint_exe = nrun#Which('eslint') " for find local eslint -let g:neomake_javascript_enabled_makers = ['eslint'] -if exists(':Neomake') - " Check for lint errors on open & write - autocmd BufRead,BufWritePost,BufEnter *.js,*.jsx,*.vue silent! Neomake standard|redraw -endif - -" make background transparent -hi Normal ctermbg=NONE -hi EndOfBuffer ctermbg=NONE -hi LineNr ctermbg=234 - -" CtrlP -let g:ctrlp_prompt_mappings={'PrtClearCache()':['']} -let g:ctrlp_prompt_mappings={'PrtdeleteEnt()':['']} -let g:ctrlp_match_window='bottom,order:btt,min:2,max:15' -set wildmenu " enhanced autocomplete -set wildignore+=*/tmp/*,*/node_modules/*,*/.git/*,*.so,*.swp,*.zip,*node_modules*,*.jpg,*.png,*.svg,*.ttf,*.woff,*.woff3,*.eot -",*public/css/*,*public/js* -" let g:ctrlp_user_command = 'find %s -type f' - -" delimitMate options -let delimitMate_expand_cr=1 - -" enable matchit (for matching tags with %) -runtime macros/matchit.vim - -" vim-sneak settings -hi SneakPluginTarget ctermfg=black ctermbg=181818 - -" javascript libraries syntax -let g:used_javascript_libs = 'vue' " underscore,react,chai, more to see -> /othree/javascript-libraries-syntax.vim - -" tern -if exists('g:plugs["tern_for_vim"]') - let g:tern_show_argument_hints = 'on_hold' - let g:tern_show_signature_in_pum = 1 - - autocmd FileType javascript setlocal omnifunc=tern#Complete -endif - -" disable colorizer at startup -let g:colorizer_startup = 0 -let g:colorizer_nomap = 1 - -" " ale (eslint) -" set nocompatible -" filetype off -" let &runtimepath.=',~/.config/nvim/plugged/ale' -" filetype plugin on -" silent! helptags ALL - -" vim-devicons -set guifont=Droid\ Sans\ Mono\ for\ Powerline\ Plus\ Nerd\ File\ Types:h11 -let g:airline_powerline_fonts = 1 - -" NERDTree -let g:NERDTreeWinPos = 'right' - -" vim-json -hi! def link jsonKeyword Identifier -let g:vim_json_syntax_conceal = 1 - -" javascript -autocmd BufRead,BufNewFile *.js setlocal filetype=javascript - -" vue -autocmd BufRead,BufNewFile *.vue setlocal filetype=vue.html.javascript.css -" let g:vue_disable_pre_processors = 1 - -" For php tab space -autocmd Filetype php setlocal ts=4 sw=4 sts=0 expandtab -autocmd Filetype blade setlocal ts=2 sw=2 sts=0 expandtab - -" vim-go -let g:deoplete#sources#go#gocode_binary = $GOPATH.'/bin/gocode' -let g:deoplete#sources#go#sort_class = ['package', 'func', 'type', 'var', 'const'] -autocmd Filetype go setlocal ts=2 sw=2 sts=0 expandtab - -" Python -" pythonmod -let g:pymode_lint = 1 -let g:pymode_python = 'python3' -" fold -autocmd BufReadPost,BufEnter *.py normal zM diff --git a/zsh/.antigenrc b/zsh/.antigenrc new file mode 100644 index 0000000..96faad7 --- /dev/null +++ b/zsh/.antigenrc @@ -0,0 +1,38 @@ +# Load oh-my-zsh library +antigen use oh-my-zsh + +# Load bundles from the default repo (oh-my-zsh) +antigen bundle git +antigen bundle command-not-found +antigen bundle docker + +antigen bundle git +antigen bundle heroku +antigen bundle pip +antigen bundle docker +antigen bundle kubernetes +antigen bundle lein +antigen bundle fzf +antigen bundle gatsby +antigen bundle httpie +antigen bundle mosh +antigen bundle npm +antigen bundle lukechilds/zsh-nvm +antigen bundle copyfile +antigen bundle copypath +antigen bundle yarn +antigen bundle alias-finder +antigen bundle command-not-found + + + +# Load bundles from external repos +antigen bundle zsh-users/zsh-completions +antigen bundle zsh-users/zsh-autosuggestions +antigen bundle zsh-users/zsh-syntax-highlighting + +# Select theme +antigen theme denysdovhan/spaceship-prompt + +# Tell Antigen that you're done +antigen apply diff --git a/zsh/.zprofile b/zsh/.zprofile new file mode 100644 index 0000000..4e34736 --- /dev/null +++ b/zsh/.zprofile @@ -0,0 +1,7 @@ +export DOTFILES="$HOME/.dotfiles" +export CONFIG="$HOME/conf" +#export LESSOPEN="|/usr/local/bin/lesspipe.sh %s" LESS_ADVANCED_PREPROCESSOR=1 + +# Created by `pipx` on 2022-07-02 11:27:20 +export PATH="$PATH:/Users/jon/.local/bin" +eval $(/opt/homebrew/bin/brew shellenv) diff --git a/zsh/.zshenv b/zsh/.zshenv new file mode 100644 index 0000000..160497c --- /dev/null +++ b/zsh/.zshenv @@ -0,0 +1,3 @@ +if [[ "$SHLVL" -eq 1 && ! -o LOGIN && -s "${ZDOTDIR:-$HOME}/.zprofile" ]]; then + source "${ZDOTDIR:-$HOME}/.zprofile" +fi From 6381ca0035711cb1dcba4c01624b0b6057efdde2 Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Sun, 31 Jul 2022 06:41:54 -0700 Subject: [PATCH 14/25] update --- nvim/autoload/.gitignore | 1 - nvim/autoload/plug.vim | 2801 ------------------------------------ nvim/autoload/plug.vim.old | 2797 ----------------------------------- 3 files changed, 5599 deletions(-) delete mode 100644 nvim/autoload/.gitignore delete mode 100644 nvim/autoload/plug.vim delete mode 100644 nvim/autoload/plug.vim.old diff --git a/nvim/autoload/.gitignore b/nvim/autoload/.gitignore deleted file mode 100644 index e774117..0000000 --- a/nvim/autoload/.gitignore +++ /dev/null @@ -1 +0,0 @@ -!plug.vim diff --git a/nvim/autoload/plug.vim b/nvim/autoload/plug.vim deleted file mode 100644 index 6a958cb..0000000 --- a/nvim/autoload/plug.vim +++ /dev/null @@ -1,2801 +0,0 @@ -" vim-plug: Vim plugin manager -" ============================ -" -" Download plug.vim and put it in ~/.vim/autoload -" -" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ -" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim -" -" Edit your .vimrc -" -" call plug#begin('~/.vim/plugged') -" -" " Make sure you use single quotes -" -" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align -" Plug 'junegunn/vim-easy-align' -" -" " Any valid git URL is allowed -" Plug 'https://github.com/junegunn/vim-github-dashboard.git' -" -" " Multiple Plug commands can be written in a single line using | separators -" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' -" -" " On-demand loading -" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } -" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } -" -" " Using a non-default branch -" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } -" -" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) -" Plug 'fatih/vim-go', { 'tag': '*' } -" -" " Plugin options -" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } -" -" " Plugin outside ~/.vim/plugged with post-update hook -" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } -" -" " Unmanaged plugin (manually installed and updated) -" Plug '~/my-prototype-plugin' -" -" " Initialize plugin system -" call plug#end() -" -" Then reload .vimrc and :PlugInstall to install plugins. -" -" Plug options: -" -"| Option | Description | -"| ----------------------- | ------------------------------------------------ | -"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | -"| `rtp` | Subdirectory that contains Vim plugin | -"| `dir` | Custom directory for the plugin | -"| `as` | Use different name for the plugin | -"| `do` | Post-update hook (string or funcref) | -"| `on` | On-demand loading: Commands or ``-mappings | -"| `for` | On-demand loading: File types | -"| `frozen` | Do not update unless explicitly specified | -" -" More information: https://github.com/junegunn/vim-plug -" -" -" Copyright (c) 2017 Junegunn Choi -" -" MIT License -" -" Permission is hereby granted, free of charge, to any person obtaining -" a copy of this software and associated documentation files (the -" "Software"), to deal in the Software without restriction, including -" without limitation the rights to use, copy, modify, merge, publish, -" distribute, sublicense, and/or sell copies of the Software, and to -" permit persons to whom the Software is furnished to do so, subject to -" the following conditions: -" -" The above copyright notice and this permission notice shall be -" included in all copies or substantial portions of the Software. -" -" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if exists('g:loaded_plug') - finish -endif -let g:loaded_plug = 1 - -let s:cpo_save = &cpo -set cpo&vim - -let s:plug_src = 'https://github.com/junegunn/vim-plug.git' -let s:plug_tab = get(s:, 'plug_tab', -1) -let s:plug_buf = get(s:, 'plug_buf', -1) -let s:mac_gui = has('gui_macvim') && has('gui_running') -let s:is_win = has('win32') -let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) -let s:vim8 = has('patch-8.0.0039') && exists('*job_start') -if s:is_win && &shellslash - set noshellslash - let s:me = resolve(expand(':p')) - set shellslash -else - let s:me = resolve(expand(':p')) -endif -let s:base_spec = { 'branch': '', 'frozen': 0 } -let s:TYPE = { -\ 'string': type(''), -\ 'list': type([]), -\ 'dict': type({}), -\ 'funcref': type(function('call')) -\ } -let s:loaded = get(s:, 'loaded', {}) -let s:triggers = get(s:, 'triggers', {}) - -function! s:is_powershell(shell) - return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$' -endfunction - -function! s:isabsolute(dir) abort - return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') -endfunction - -function! s:git_dir(dir) abort - let gitdir = s:trim(a:dir) . '/.git' - if isdirectory(gitdir) - return gitdir - endif - if !filereadable(gitdir) - return '' - endif - let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') - if len(gitdir) && !s:isabsolute(gitdir) - let gitdir = a:dir . '/' . gitdir - endif - return isdirectory(gitdir) ? gitdir : '' -endfunction - -function! s:git_origin_url(dir) abort - let gitdir = s:git_dir(a:dir) - let config = gitdir . '/config' - if empty(gitdir) || !filereadable(config) - return '' - endif - return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') -endfunction - -function! s:git_revision(dir) abort - let gitdir = s:git_dir(a:dir) - let head = gitdir . '/HEAD' - if empty(gitdir) || !filereadable(head) - return '' - endif - - let line = get(readfile(head), 0, '') - let ref = matchstr(line, '^ref: \zs.*') - if empty(ref) - return line - endif - - if filereadable(gitdir . '/' . ref) - return get(readfile(gitdir . '/' . ref), 0, '') - endif - - if filereadable(gitdir . '/packed-refs') - for line in readfile(gitdir . '/packed-refs') - if line =~# ' ' . ref - return matchstr(line, '^[0-9a-f]*') - endif - endfor - endif - - return '' -endfunction - -function! s:git_local_branch(dir) abort - let gitdir = s:git_dir(a:dir) - let head = gitdir . '/HEAD' - if empty(gitdir) || !filereadable(head) - return '' - endif - let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') - return len(branch) ? branch : 'HEAD' -endfunction - -function! s:git_origin_branch(spec) - if len(a:spec.branch) - return a:spec.branch - endif - - " The file may not be present if this is a local repository - let gitdir = s:git_dir(a:spec.dir) - let origin_head = gitdir.'/refs/remotes/origin/HEAD' - if len(gitdir) && filereadable(origin_head) - return matchstr(get(readfile(origin_head), 0, ''), - \ '^ref: refs/remotes/origin/\zs.*') - endif - - " The command may not return the name of a branch in detached HEAD state - let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) - return v:shell_error ? '' : result[-1] -endfunction - -if s:is_win - function! s:plug_call(fn, ...) - let shellslash = &shellslash - try - set noshellslash - return call(a:fn, a:000) - finally - let &shellslash = shellslash - endtry - endfunction -else - function! s:plug_call(fn, ...) - return call(a:fn, a:000) - endfunction -endif - -function! s:plug_getcwd() - return s:plug_call('getcwd') -endfunction - -function! s:plug_fnamemodify(fname, mods) - return s:plug_call('fnamemodify', a:fname, a:mods) -endfunction - -function! s:plug_expand(fmt) - return s:plug_call('expand', a:fmt, 1) -endfunction - -function! s:plug_tempname() - return s:plug_call('tempname') -endfunction - -function! plug#begin(...) - if a:0 > 0 - let s:plug_home_org = a:1 - let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) - elseif exists('g:plug_home') - let home = s:path(g:plug_home) - elseif !empty(&rtp) - let home = s:path(split(&rtp, ',')[0]) . '/plugged' - else - return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') - endif - if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp - return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') - endif - - let g:plug_home = home - let g:plugs = {} - let g:plugs_order = [] - let s:triggers = {} - - call s:define_commands() - return 1 -endfunction - -function! s:define_commands() - command! -nargs=+ -bar Plug call plug#() - if !executable('git') - return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') - endif - if has('win32') - \ && &shellslash - \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell)) - return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') - endif - if !has('nvim') - \ && (has('win32') || has('win32unix')) - \ && !has('multi_byte') - return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') - endif - command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) - command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) - command! -nargs=0 -bar -bang PlugClean call s:clean(0) - 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! s:lazy(plug, opt) - return has_key(a:plug, a:opt) && - \ (empty(s:to_a(a:plug[a:opt])) || - \ !isdirectory(a:plug.dir) || - \ len(s:glob(s:rtp(a:plug), 'plugin')) || - \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) -endfunction - -function! plug#end() - if !exists('g:plugs') - return s:err('plug#end() called without calling plug#begin() first') - endif - - if exists('#PlugLOD') - augroup PlugLOD - autocmd! - augroup END - augroup! PlugLOD - endif - let lod = { 'ft': {}, 'map': {}, 'cmd': {} } - - if exists('g:did_load_filetypes') - filetype off - endif - for name in g:plugs_order - if !has_key(g:plugs, name) - continue - endif - let plug = g:plugs[name] - if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') - let s:loaded[name] = 1 - continue - endif - - if has_key(plug, 'on') - let s:triggers[name] = { 'map': [], 'cmd': [] } - for cmd in s:to_a(plug.on) - if cmd =~? '^.\+' - 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 - -function! s:rtp(spec) - return s:path(a:spec.dir . get(a:spec, 'rtp', '')) -endfunction - -if s:is_win - function! s:path(path) - return s:trim(substitute(a:path, '/', '\', 'g')) - endfunction - - function! s:dirpath(path) - return s:path(a:path) . '\' - endfunction - - function! s:is_local_plug(repo) - return a:repo =~? '^[a-z]:\|^[%~]' - endfunction - - " Copied from fzf - function! s:wrap_cmds(cmds) - let cmds = [ - \ '@echo off', - \ 'setlocal enabledelayedexpansion'] - \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) - \ + ['endlocal'] - if has('iconv') - if !exists('s:codepage') - let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) - endif - return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) - endif - return map(cmds, 'v:val."\r"') - endfunction - - function! s:batchfile(cmd) - let batchfile = s:plug_tempname().'.bat' - call writefile(s:wrap_cmds(a:cmd), batchfile) - let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) - if s:is_powershell(&shell) - let cmd = '& ' . cmd - endif - return [batchfile, cmd] - endfunction -else - function! s:path(path) - return s:trim(a:path) - endfunction - - function! s:dirpath(path) - return substitute(a:path, '[/\\]*$', '/', '') - endfunction - - function! s:is_local_plug(repo) - return a:repo[0] =~ '[/$~]' - endfunction -endif - -function! s:err(msg) - echohl ErrorMsg - echom '[vim-plug] '.a:msg - echohl None -endfunction - -function! s:warn(cmd, msg) - echohl WarningMsg - execute a:cmd 'a:msg' - echohl None -endfunction - -function! s:esc(path) - return escape(a:path, ' ') -endfunction - -function! s:escrtp(path) - return escape(a:path, ' ,') -endfunction - -function! s:remove_rtp() - for name in s:loaded_names() - let rtp = s:rtp(g:plugs[name]) - execute 'set rtp-='.s:escrtp(rtp) - let after = globpath(rtp, 'after') - if isdirectory(after) - execute 'set rtp-='.s:escrtp(after) - endif - endfor -endfunction - -function! s:reorg_rtp() - if !empty(s:first_rtp) - execute 'set rtp-='.s:first_rtp - execute 'set rtp-='.s:last_rtp - endif - - " &rtp is modified from outside - if exists('s:prtp') && s:prtp !=# &rtp - call s:remove_rtp() - unlet! s:middle - endif - - let s:middle = get(s:, 'middle', &rtp) - let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') - let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') - let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') - \ . ','.s:middle.',' - \ . join(map(afters, 'escape(v:val, ",")'), ',') - let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') - let s:prtp = &rtp - - if !empty(s:first_rtp) - execute 'set rtp^='.s:first_rtp - execute 'set rtp+='.s:last_rtp - endif -endfunction - -function! s:doautocmd(...) - if exists('#'.join(a:000, '#')) - execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) - endif -endfunction - -function! s:dobufread(names) - for name in a:names - let path = s:rtp(g:plugs[name]) - for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] - if len(finddir(dir, path)) - if exists('#BufRead') - doautocmd BufRead - endif - return - endif - endfor - endfor -endfunction - -function! plug#load(...) - if a:0 == 0 - return s:err('Argument missing: plugin name(s) required') - endif - if !exists('g:plugs') - return s:err('plug#begin was not called') - endif - let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 - let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') - if !empty(unknowns) - let s = len(unknowns) > 1 ? 's' : '' - return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) - end - let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') - if !empty(unloaded) - for name in unloaded - call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - endfor - call s:dobufread(unloaded) - return 1 - end - return 0 -endfunction - -function! s:remove_triggers(name) - if !has_key(s:triggers, a:name) - return - endif - for cmd in s:triggers[a:name].cmd - execute 'silent! delc' cmd - endfor - for map in s:triggers[a:name].map - execute 'silent! unmap' map - execute 'silent! iunmap' map - endfor - call remove(s:triggers, a:name) -endfunction - -function! s:lod(names, types, ...) - for name in a:names - call s:remove_triggers(name) - let s:loaded[name] = 1 - endfor - call s:reorg_rtp() - - for name in a:names - let rtp = s:rtp(g:plugs[name]) - for dir in a:types - call s:source(rtp, dir.'/**/*.vim') - endfor - if a:0 - if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) - execute 'runtime' a:1 - endif - call s:source(rtp, a:2) - endif - call s:doautocmd('User', name) - endfor -endfunction - -function! s:lod_ft(pat, names) - let syn = 'syntax/'.a:pat.'.vim' - call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) - execute 'autocmd! PlugLOD FileType' a:pat - call s:doautocmd('filetypeplugin', 'FileType') - call s:doautocmd('filetypeindent', 'FileType') -endfunction - -function! s:lod_cmd(cmd, bang, l1, l2, args, names) - call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - call s:dobufread(a:names) - execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) -endfunction - -function! s:lod_map(map, names, with_prefix, prefix) - call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - call s:dobufread(a:names) - let extra = '' - while 1 - let c = getchar(0) - if c == 0 - break - endif - let extra .= nr2char(c) - endwhile - - if a:with_prefix - let prefix = v:count ? v:count : '' - let prefix .= '"'.v:register.a:prefix - if mode(1) == 'no' - if v:operator == 'c' - let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) - let spec = extend(s:infer_properties(name, repo), opts) - if !has_key(g:plugs, name) - call add(g:plugs_order, name) - endif - let g:plugs[name] = spec - let s:loaded[name] = get(s:loaded, name, 0) - catch - return s:err(repo . ' ' . v:exception) - endtry -endfunction - -function! s:parse_options(arg) - let opts = copy(s:base_spec) - let type = type(a:arg) - let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' - if type == s:TYPE.string - if empty(a:arg) - throw printf(opt_errfmt, 'tag', 'string') - endif - let opts.tag = a:arg - elseif type == s:TYPE.dict - for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] - if has_key(a:arg, opt) - \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) - throw printf(opt_errfmt, opt, 'string') - endif - endfor - for opt in ['on', 'for'] - if has_key(a:arg, opt) - \ && type(a:arg[opt]) != s:TYPE.list - \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) - throw printf(opt_errfmt, opt, 'string or list') - endif - endfor - if has_key(a:arg, 'do') - \ && type(a:arg.do) != s:TYPE.funcref - \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) - throw printf(opt_errfmt, 'do', 'string or funcref') - endif - call extend(opts, a:arg) - if has_key(opts, 'dir') - let opts.dir = s:dirpath(s:plug_expand(opts.dir)) - endif - else - throw 'Invalid argument type (expected: string or dictionary)' - endif - return opts -endfunction - -function! s:infer_properties(name, repo) - let repo = a:repo - if s:is_local_plug(repo) - return { 'dir': s:dirpath(s:plug_expand(repo)) } - else - if repo =~ ':' - let uri = repo - else - if repo !~ '/' - throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) - endif - let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') - let uri = printf(fmt, repo) - endif - return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } - endif -endfunction - -function! s:install(force, names) - call s:update_impl(0, a:force, a:names) -endfunction - -function! s:update(force, names) - call s:update_impl(1, a:force, a:names) -endfunction - -function! plug#helptags() - if !exists('g:plugs') - return s:err('plug#begin was not called') - endif - for spec in values(g:plugs) - let docd = join([s:rtp(spec), 'doc'], '/') - if isdirectory(docd) - silent! execute 'helptags' s:esc(docd) - endif - endfor - return 1 -endfunction - -function! s:syntax() - syntax clear - syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber - syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX - syn match plugNumber /[0-9]\+[0-9.]*/ contained - syn match plugBracket /[[\]]/ contained - syn match plugX /x/ contained - syn match plugDash /^-\{1}\ / - syn match plugPlus /^+/ - syn match plugStar /^*/ - syn match plugMessage /\(^- \)\@<=.*/ - syn match plugName /\(^- \)\@<=[^ ]*:/ - syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ - syn match plugTag /(tag: [^)]\+)/ - syn match plugInstall /\(^+ \)\@<=[^:]*/ - syn match plugUpdate /\(^* \)\@<=[^:]*/ - syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag - syn match plugEdge /^ \X\+$/ - syn match plugEdge /^ \X*/ contained nextgroup=plugSha - syn match plugSha /[0-9a-f]\{7,9}/ contained - syn match plugRelDate /([^)]*)$/ contained - syn match plugNotLoaded /(not loaded)$/ - syn match plugError /^x.*/ - syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ - syn match plugH2 /^.*:\n-\+$/ - syn match plugH2 /^-\{2,}/ - syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean - hi def link plug1 Title - hi def link plug2 Repeat - hi def link plugH2 Type - hi def link plugX Exception - hi def link plugBracket Structure - hi def link plugNumber Number - - hi def link plugDash Special - hi def link plugPlus Constant - hi def link plugStar Boolean - - hi def link plugMessage Function - hi def link plugName Label - hi def link plugInstall Function - hi def link plugUpdate Type - - hi def link plugError Error - hi def link plugDeleted Ignore - hi def link plugRelDate Comment - hi def link plugEdge PreProc - hi def link plugSha Identifier - hi def link plugTag Constant - - hi def link plugNotLoaded Comment -endfunction - -function! s:lpad(str, len) - return a:str . repeat(' ', a:len - len(a:str)) -endfunction - -function! s:lines(msg) - return split(a:msg, "[\r\n]") -endfunction - -function! s:lastline(msg) - return get(s:lines(a:msg), -1, '') -endfunction - -function! s:new_window() - execute get(g:, 'plug_window', 'vertical topleft new') -endfunction - -function! s:plug_window_exists() - let buflist = tabpagebuflist(s:plug_tab) - return !empty(buflist) && index(buflist, s:plug_buf) >= 0 -endfunction - -function! s:switch_in() - if !s:plug_window_exists() - return 0 - endif - - if winbufnr(0) != s:plug_buf - let s:pos = [tabpagenr(), winnr(), winsaveview()] - execute 'normal!' s:plug_tab.'gt' - let winnr = bufwinnr(s:plug_buf) - execute winnr.'wincmd w' - call add(s:pos, winsaveview()) - else - let s:pos = [winsaveview()] - endif - - setlocal modifiable - return 1 -endfunction - -function! s:switch_out(...) - call winrestview(s:pos[-1]) - setlocal nomodifiable - if a:0 > 0 - execute a:1 - endif - - if len(s:pos) > 1 - execute 'normal!' s:pos[0].'gt' - execute s:pos[1] 'wincmd w' - call winrestview(s:pos[2]) - endif -endfunction - -function! s:finish_bindings() - nnoremap 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(s:plug_getcwd()) - throw 'Invalid current working directory. Cannot proceed.' - endif - - for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] - if exists(evar) - throw evar.' detected. Cannot proceed.' - endif - endfor - - call s:job_abort() - if s:switch_in() - if b:plug_preview == 1 - pc - endif - enew - else - call s:new_window() - endif - - nnoremap q :call close_pane() - 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 - if exists('+colorcolumn') - setlocal colorcolumn= - endif - setf vim-plug - if exists('g:syntax_on') - call s:syntax() - endif -endfunction - -function! s:close_pane() - if b:plug_preview == 1 - pc - let b:plug_preview = -1 - else - bd - endif -endfunction - -function! s:assign_name() - " Assign buffer name - let prefix = '[Plugins]' - let name = prefix - let idx = 2 - while bufexists(name) - let name = printf('%s (%s)', prefix, idx) - let idx = idx + 1 - endwhile - silent! execute 'f' fnameescape(name) -endfunction - -function! s:chsh(swap) - let prev = [&shell, &shellcmdflag, &shellredir] - if !s:is_win - set shell=sh - endif - if a:swap - if s:is_powershell(&shell) - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' - elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' - set shellredir=>%s\ 2>&1 - endif - endif - return prev -endfunction - -function! s:bang(cmd, ...) - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(a:0) - " FIXME: Escaping is incomplete. We could use shellescape with eval, - " but it won't work on Windows. - let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd - if s:is_win - let [batchfile, cmd] = s:batchfile(cmd) - endif - let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') - execute "normal! :execute g:_plug_bang\\" - finally - unlet g:_plug_bang - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry - return v:shell_error ? 'Exit status: ' . v:shell_error : '' -endfunction - -function! s:regress_bar() - let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') - call s:progress_bar(2, bar, len(bar)) -endfunction - -function! s:is_updated(dir) - return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) -endfunction - -function! s:do(pull, force, todo) - for [name, spec] in items(a:todo) - if !isdirectory(spec.dir) - continue - endif - let installed = has_key(s:update.new, name) - let updated = installed ? 0 : - \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) - if a:force || installed || updated - execute 'cd' s:esc(spec.dir) - call append(3, '- Post-update hook for '. name .' ... ') - let error = '' - let type = type(spec.do) - if type == s:TYPE.string - if spec.do[0] == ':' - if !get(s:loaded, name, 0) - let s:loaded[name] = 1 - call s:reorg_rtp() - endif - call s:load_plugin(spec) - try - execute spec.do[1:] - catch - let error = v:exception - endtry - if !s:plug_window_exists() - cd - - throw 'Warning: vim-plug was terminated by the post-update hook of '.name - endif - else - let error = s:bang(spec.do) - endif - elseif type == s:TYPE.funcref - try - call s:load_plugin(spec) - let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') - call spec.do({ 'name': name, 'status': status, 'force': a:force }) - catch - let error = v:exception - endtry - else - let error = 'Invalid hook type' - endif - call s:switch_in() - call setline(4, empty(error) ? (getline(4) . 'OK') - \ : ('x' . getline(4)[1:] . error)) - if !empty(error) - call add(s:update.errors, name) - call s:regress_bar() - endif - cd - - endif - endfor -endfunction - -function! s:hash_match(a, b) - return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 -endfunction - -function! s:checkout(spec) - let sha = a:spec.commit - let output = s:git_revision(a:spec.dir) - if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) - let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' - let output = s:system( - \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) - endif - return output -endfunction - -function! s:finish(pull) - let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) - if new_frozen - let s = new_frozen > 1 ? 's' : '' - call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) - endif - call append(3, '- Finishing ... ') | 4 - redraw - call plug#helptags() - call plug#end() - call setline(4, getline(4) . 'Done!') - redraw - let msgs = [] - if !empty(s:update.errors) - call add(msgs, "Press 'R' to retry.") - endif - if a:pull && len(s:update.new) < len(filter(getline(5, '$'), - \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) - call add(msgs, "Press 'D' to see the updated changes.") - endif - echo join(msgs, ' ') - call s:finish_bindings() -endfunction - -function! s:retry() - if empty(s:update.errors) - return - endif - echo - call s:update_impl(s:update.pull, s:update.force, - \ extend(copy(s:update.errors), [s:update.threads])) -endfunction - -function! s:is_managed(name) - return has_key(g:plugs[a:name], 'uri') -endfunction - -function! s:names(...) - return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) -endfunction - -function! s:check_ruby() - silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") - if !exists('g:plug_ruby') - redraw! - return s:warn('echom', 'Warning: Ruby interface is broken') - endif - let ruby_version = split(g:plug_ruby, '\.') - unlet g:plug_ruby - return s:version_requirement(ruby_version, [1, 8, 7]) -endfunction - -function! s:update_impl(pull, force, args) abort - let sync = index(a:args, '--sync') >= 0 || has('vim_starting') - let args = filter(copy(a:args), 'v:val != "--sync"') - let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? - \ remove(args, -1) : get(g:, 'plug_threads', 16) - - let managed = filter(copy(g:plugs), 's:is_managed(v:key)') - let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : - \ filter(managed, 'index(args, v:key) >= 0') - - if empty(todo) - return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) - endif - - if !s:is_win && s:git_version_requirement(2, 3) - let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' - let $GIT_TERMINAL_PROMPT = 0 - for plug in values(todo) - let plug.uri = substitute(plug.uri, - \ '^https://git::@github\.com', 'https://github.com', '') - endfor - endif - - if !isdirectory(g:plug_home) - try - call mkdir(g:plug_home, 'p') - catch - return s:err(printf('Invalid plug directory: %s. '. - \ 'Try to call plug#begin with a valid directory', g:plug_home)) - endtry - endif - - if has('nvim') && !exists('*jobwait') && threads > 1 - call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') - endif - - let use_job = s:nvim || s:vim8 - let python = (has('python') || has('python3')) && !use_job - let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() - - let s:update = { - \ 'start': reltime(), - \ 'all': todo, - \ 'todo': copy(todo), - \ 'errors': [], - \ 'pull': a:pull, - \ 'force': a:force, - \ 'new': {}, - \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, - \ 'bar': '', - \ 'fin': 0 - \ } - - call s:prepare(1) - call append(0, ['', '']) - normal! 2G - silent! redraw - - let s:clone_opt = [] - if get(g:, 'plug_shallow', 1) - call extend(s:clone_opt, ['--depth', '1']) - if s:git_version_requirement(1, 7, 10) - call add(s:clone_opt, '--no-single-branch') - endif - endif - - if has('win32unix') || has('wsl') - call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) - endif - - let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' - - " Python version requirement (>= 2.7) - if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 - redir => pyv - silent python import platform; print platform.python_version() - redir END - let python = s:version_requirement( - \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) - endif - - if (python || ruby) && s:update.threads > 1 - try - let imd = &imd - if s:mac_gui - set noimd - endif - if ruby - call s:update_ruby() - else - call s:update_python() - endif - catch - let lines = getline(4, '$') - let printed = {} - silent! 4,$d _ - for line in lines - let name = s:extract_name(line, '.', '') - if empty(name) || !has_key(printed, name) - call append('$', line) - if !empty(name) - let printed[name] = 1 - if line[0] == 'x' && index(s:update.errors, name) < 0 - call add(s:update.errors, name) - end - endif - endif - endfor - finally - let &imd = imd - call s:update_finish() - endtry - else - call s:update_vim() - while use_job && sync - sleep 100m - if s:update.fin - break - endif - endwhile - endif -endfunction - -function! s:log4(name, msg) - call setline(4, printf('- %s (%s)', a:msg, a:name)) - redraw -endfunction - -function! s:update_finish() - if exists('s:git_terminal_prompt') - let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt - endif - if s:switch_in() - call append(3, '- Updating ...') | 4 - for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) - let [pos, _] = s:logpos(name) - if !pos - continue - endif - if has_key(spec, 'commit') - call s:log4(name, 'Checking out '.spec.commit) - let out = s:checkout(spec) - elseif has_key(spec, 'tag') - let tag = spec.tag - if tag =~ '\*' - let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) - if !v:shell_error && !empty(tags) - let tag = tags[0] - call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) - call append(3, '') - endif - endif - call s:log4(name, 'Checking out '.tag) - let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) - else - let branch = s:git_origin_branch(spec) - call s:log4(name, 'Merging origin/'.s:esc(branch)) - let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' - \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) - endif - if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && - \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) - call s:log4(name, 'Updating submodules. This may take a while.') - let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) - endif - let msg = s:format_message(v:shell_error ? 'x': '-', name, out) - if v:shell_error - call add(s:update.errors, name) - call s:regress_bar() - silent execute pos 'd _' - call append(4, msg) | 4 - elseif !empty(out) - call setline(pos, msg[0]) - endif - redraw - endfor - silent 4 d _ - try - call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) - catch - call s:warn('echom', v:exception) - call s:warn('echo', '') - return - endtry - call s:finish(s:update.pull) - call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') - call s:switch_out('normal! gg') - endif -endfunction - -function! s:job_abort() - if (!s:nvim && !s:vim8) || !exists('s:jobs') - return - endif - - for [name, j] in items(s:jobs) - if s:nvim - silent! call jobstop(j.jobid) - elseif s:vim8 - silent! call job_stop(j.jobid) - endif - if j.new - call s:rm_rf(g:plugs[name].dir) - endif - endfor - let s:jobs = {} -endfunction - -function! s:last_non_empty_line(lines) - let len = len(a:lines) - for idx in range(len) - let line = a:lines[len-idx-1] - if !empty(line) - return line - endif - endfor - return '' -endfunction - -function! s:job_out_cb(self, data) abort - let self = a:self - let data = remove(self.lines, -1) . a:data - let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') - call extend(self.lines, lines) - " To reduce the number of buffer updates - let self.tick = get(self, 'tick', -1) + 1 - if !self.running || self.tick % len(s:jobs) == 0 - let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') - let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) - call s:log(bullet, self.name, result) - endif -endfunction - -function! s:job_exit_cb(self, data) abort - let a:self.running = 0 - let a:self.error = a:data != 0 - call s:reap(a:self.name) - call s:tick() -endfunction - -function! s:job_cb(fn, job, ch, data) - if !s:plug_window_exists() " plug window closed - return s:job_abort() - endif - call call(a:fn, [a:job, a:data]) -endfunction - -function! s:nvim_cb(job_id, data, event) dict abort - return (a:event == 'stdout' || a:event == 'stderr') ? - \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : - \ s:job_cb('s:job_exit_cb', self, 0, a:data) -endfunction - -function! s:spawn(name, cmd, opts) - let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], - \ 'new': get(a:opts, 'new', 0) } - let s:jobs[a:name] = job - - if s:nvim - if has_key(a:opts, 'dir') - let job.cwd = a:opts.dir - endif - let argv = a:cmd - call extend(job, { - \ 'on_stdout': function('s:nvim_cb'), - \ 'on_stderr': function('s:nvim_cb'), - \ 'on_exit': function('s:nvim_cb'), - \ }) - let jid = s:plug_call('jobstart', argv, job) - if jid > 0 - let job.jobid = jid - else - let job.running = 0 - let job.error = 1 - let job.lines = [jid < 0 ? argv[0].' is not executable' : - \ 'Invalid arguments (or job table is full)'] - endif - elseif s:vim8 - let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) - if has_key(a:opts, 'dir') - let cmd = s:with_cd(cmd, a:opts.dir, 0) - endif - let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] - let jid = job_start(s:is_win ? join(argv, ' ') : argv, { - \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), - \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), - \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), - \ 'err_mode': 'raw', - \ 'out_mode': 'raw' - \}) - if job_status(jid) == 'run' - let job.jobid = jid - else - let job.running = 0 - let job.error = 1 - let job.lines = ['Failed to start job'] - endif - else - let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) - let job.error = v:shell_error != 0 - let job.running = 0 - endif -endfunction - -function! s:reap(name) - let job = s:jobs[a:name] - if job.error - call add(s:update.errors, a:name) - elseif get(job, 'new', 0) - let s:update.new[a:name] = 1 - endif - let s:update.bar .= job.error ? 'x' : '=' - - let bullet = job.error ? 'x' : '-' - let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) - call s:log(bullet, a:name, empty(result) ? 'OK' : result) - call s:bar() - - call remove(s:jobs, a:name) -endfunction - -function! s:bar() - if s:switch_in() - let total = len(s:update.all) - call setline(1, (s:update.pull ? 'Updating' : 'Installing'). - \ ' plugins ('.len(s:update.bar).'/'.total.')') - call s:progress_bar(2, s:update.bar, total) - call s:switch_out() - endif -endfunction - -function! s:logpos(name) - let max = line('$') - for i in range(4, max > 4 ? max : 4) - if getline(i) =~# '^[-+x*] '.a:name.':' - for j in range(i + 1, max > 5 ? max : 5) - if getline(j) !~ '^ ' - return [i, j - 1] - endif - endfor - return [i, i] - endif - endfor - return [0, 0] -endfunction - -function! s:log(bullet, name, lines) - if s:switch_in() - let [b, e] = s:logpos(a:name) - if b > 0 - silent execute printf('%d,%d d _', b, e) - if b > winheight('.') - let b = 4 - endif - else - let b = 4 - endif - " FIXME For some reason, nomodifiable is set after :d in vim8 - setlocal modifiable - call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) - call s:switch_out() - endif -endfunction - -function! s:update_vim() - let s:jobs = {} - - call s:bar() - call s:tick() -endfunction - -function! s:tick() - let pull = s:update.pull - let prog = s:progress_opt(s:nvim || s:vim8) -while 1 " Without TCO, Vim stack is bound to explode - if empty(s:update.todo) - if empty(s:jobs) && !s:update.fin - call s:update_finish() - let s:update.fin = 1 - endif - return - endif - - let name = keys(s:update.todo)[0] - let spec = remove(s:update.todo, name) - let new = empty(globpath(spec.dir, '.git', 1)) - - call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') - redraw - - let has_tag = has_key(spec, 'tag') - if !new - let [error, _] = s:git_validate(spec, 0) - if empty(error) - if pull - let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] - if has_tag && !empty(globpath(spec.dir, '.git/shallow')) - call extend(cmd, ['--depth', '99999999']) - endif - if !empty(prog) - call add(cmd, prog) - endif - call s:spawn(name, cmd, { 'dir': spec.dir }) - else - let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } - endif - else - let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } - endif - else - let cmd = ['git', 'clone'] - if !has_tag - call extend(cmd, s:clone_opt) - endif - if !empty(prog) - call add(cmd, prog) - endif - call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) - endif - - if !s:jobs[name].running - call s:reap(name) - endif - if len(s:jobs) >= s:update.threads - break - endif -endwhile -endfunction - -function! s:update_python() -let py_exe = has('python') ? 'python' : 'python3' -execute py_exe "<< EOF" -import datetime -import functools -import os -try: - import queue -except ImportError: - import Queue as queue -import random -import re -import shutil -import signal -import subprocess -import tempfile -import threading as thr -import time -import traceback -import vim - -G_NVIM = vim.eval("has('nvim')") == '1' -G_PULL = vim.eval('s:update.pull') == '1' -G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 -G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) -G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) -G_PROGRESS = vim.eval('s:progress_opt(1)') -G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) -G_STOP = thr.Event() -G_IS_WIN = vim.eval('s:is_win') == '1' - -class PlugError(Exception): - def __init__(self, msg): - self.msg = msg -class CmdTimedOut(PlugError): - pass -class CmdFailed(PlugError): - pass -class InvalidURI(PlugError): - pass -class Action(object): - INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] - -class Buffer(object): - def __init__(self, lock, num_plugs, is_pull): - self.bar = '' - self.event = 'Updating' if is_pull else 'Installing' - self.lock = lock - self.maxy = int(vim.eval('winheight(".")')) - self.num_plugs = num_plugs - - def __where(self, name): - """ Find first line with name in current buffer. Return line num. """ - found, lnum = False, 0 - matcher = re.compile('^[-+x*] {0}:'.format(name)) - for line in vim.current.buffer: - if matcher.search(line) is not None: - found = True - break - lnum += 1 - - if not found: - lnum = -1 - return lnum - - def header(self): - curbuf = vim.current.buffer - curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) - - num_spaces = self.num_plugs - len(self.bar) - curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') - - with self.lock: - vim.command('normal! 2G') - vim.command('redraw') - - def write(self, action, name, lines): - first, rest = lines[0], lines[1:] - msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] - msg.extend([' ' + line for line in rest]) - - try: - if action == Action.ERROR: - self.bar += 'x' - vim.command("call add(s:update.errors, '{0}')".format(name)) - elif action == Action.DONE: - self.bar += '=' - - curbuf = vim.current.buffer - lnum = self.__where(name) - if lnum != -1: # Found matching line num - del curbuf[lnum] - if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): - lnum = 3 - else: - lnum = 3 - curbuf.append(msg, lnum) - - self.header() - except vim.error: - pass - -class Command(object): - CD = 'cd /d' if G_IS_WIN else 'cd' - - def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): - self.cmd = cmd - if cmd_dir: - self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) - self.timeout = timeout - self.callback = cb if cb else (lambda msg: None) - self.clean = clean if clean else (lambda: None) - self.proc = None - - @property - def alive(self): - """ Returns true only if command still running. """ - return self.proc and self.proc.poll() is None - - def execute(self, ntries=3): - """ Execute the command with ntries if CmdTimedOut. - Returns the output of the command if no Exception. - """ - attempt, finished, limit = 0, False, self.timeout - - while not finished: - try: - attempt += 1 - result = self.try_command() - finished = True - return result - except CmdTimedOut: - if attempt != ntries: - self.notify_retry() - self.timeout += limit - else: - raise - - def notify_retry(self): - """ Retry required for command, notify user. """ - for count in range(3, 0, -1): - if G_STOP.is_set(): - raise KeyboardInterrupt - msg = 'Timeout. Will retry in {0} second{1} ...'.format( - count, 's' if count != 1 else '') - self.callback([msg]) - time.sleep(1) - self.callback(['Retrying ...']) - - def try_command(self): - """ Execute a cmd & poll for callback. Returns list of output. - Raises CmdFailed -> return code for Popen isn't 0 - Raises CmdTimedOut -> command exceeded timeout without new output - """ - first_line = True - - try: - tfile = tempfile.NamedTemporaryFile(mode='w+b') - preexec_fn = not G_IS_WIN and os.setsid or None - self.proc = subprocess.Popen(self.cmd, stdout=tfile, - stderr=subprocess.STDOUT, - stdin=subprocess.PIPE, shell=True, - preexec_fn=preexec_fn) - thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) - thrd.start() - - thread_not_started = True - while thread_not_started: - try: - thrd.join(0.1) - thread_not_started = False - except RuntimeError: - pass - - while self.alive: - if G_STOP.is_set(): - raise KeyboardInterrupt - - if first_line or random.random() < G_LOG_PROB: - first_line = False - line = '' if G_IS_WIN else nonblock_read(tfile.name) - if line: - self.callback([line]) - - time_diff = time.time() - os.path.getmtime(tfile.name) - if time_diff > self.timeout: - raise CmdTimedOut(['Timeout!']) - - thrd.join(0.5) - - tfile.seek(0) - result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] - - if self.proc.returncode != 0: - raise CmdFailed([''] + result) - - return result - except: - self.terminate() - raise - - def terminate(self): - """ Terminate process and cleanup. """ - if self.alive: - if G_IS_WIN: - os.kill(self.proc.pid, signal.SIGINT) - else: - os.killpg(self.proc.pid, signal.SIGTERM) - self.clean() - -class Plugin(object): - def __init__(self, name, args, buf_q, lock): - self.name = name - self.args = args - self.buf_q = buf_q - self.lock = lock - self.tag = args.get('tag', 0) - - def manage(self): - try: - if os.path.exists(self.args['dir']): - self.update() - else: - self.install() - with self.lock: - thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) - except PlugError as exc: - self.write(Action.ERROR, self.name, exc.msg) - except KeyboardInterrupt: - G_STOP.set() - self.write(Action.ERROR, self.name, ['Interrupted!']) - except: - # Any exception except those above print stack trace - msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) - self.write(Action.ERROR, self.name, msg.split('\n')) - raise - - def install(self): - target = self.args['dir'] - if target[-1] == '\\': - target = target[0:-1] - - def clean(target): - def _clean(): - try: - shutil.rmtree(target) - except OSError: - pass - return _clean - - self.write(Action.INSTALL, self.name, ['Installing ...']) - callback = functools.partial(self.write, Action.INSTALL, self.name) - cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( - '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], - esc(target)) - com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) - result = com.execute(G_RETRIES) - self.write(Action.DONE, self.name, result[-1:]) - - def repo_uri(self): - cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' - command = Command(cmd, self.args['dir'], G_TIMEOUT,) - result = command.execute(G_RETRIES) - return result[-1] - - def update(self): - actual_uri = self.repo_uri() - expect_uri = self.args['uri'] - regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') - ma = regex.match(actual_uri) - mb = regex.match(expect_uri) - if ma is None or mb is None or ma.groups() != mb.groups(): - msg = ['', - 'Invalid URI: {0}'.format(actual_uri), - 'Expected {0}'.format(expect_uri), - 'PlugClean required.'] - raise InvalidURI(msg) - - if G_PULL: - self.write(Action.UPDATE, self.name, ['Updating ...']) - callback = functools.partial(self.write, Action.UPDATE, self.name) - fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' - cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) - com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) - result = com.execute(G_RETRIES) - self.write(Action.DONE, self.name, result[-1:]) - else: - self.write(Action.DONE, self.name, ['Already installed']) - - def write(self, action, name, msg): - self.buf_q.put((action, name, msg)) - -class PlugThread(thr.Thread): - def __init__(self, tname, args): - super(PlugThread, self).__init__() - self.tname = tname - self.args = args - - def run(self): - thr.current_thread().name = self.tname - buf_q, work_q, lock = self.args - - try: - while not G_STOP.is_set(): - name, args = work_q.get_nowait() - plug = Plugin(name, args, buf_q, lock) - plug.manage() - work_q.task_done() - except queue.Empty: - pass - -class RefreshThread(thr.Thread): - def __init__(self, lock): - super(RefreshThread, self).__init__() - self.lock = lock - self.running = True - - def run(self): - while self.running: - with self.lock: - thread_vim_command('noautocmd normal! a') - time.sleep(0.33) - - def stop(self): - self.running = False - -if G_NVIM: - def thread_vim_command(cmd): - vim.session.threadsafe_call(lambda: vim.command(cmd)) -else: - def thread_vim_command(cmd): - vim.command(cmd) - -def esc(name): - return '"' + name.replace('"', '\"') + '"' - -def nonblock_read(fname): - """ Read a file with nonblock flag. Return the last line. """ - fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) - buf = os.read(fread, 100000).decode('utf-8', 'replace') - os.close(fread) - - line = buf.rstrip('\r\n') - left = max(line.rfind('\r'), line.rfind('\n')) - if left != -1: - left += 1 - line = line[left:] - - return line - -def main(): - thr.current_thread().name = 'main' - nthreads = int(vim.eval('s:update.threads')) - plugs = vim.eval('s:update.todo') - mac_gui = vim.eval('s:mac_gui') == '1' - - lock = thr.Lock() - buf = Buffer(lock, len(plugs), G_PULL) - buf_q, work_q = queue.Queue(), queue.Queue() - for work in plugs.items(): - work_q.put(work) - - start_cnt = thr.active_count() - for num in range(nthreads): - tname = 'PlugT-{0:02}'.format(num) - thread = PlugThread(tname, (buf_q, work_q, lock)) - thread.start() - if mac_gui: - rthread = RefreshThread(lock) - rthread.start() - - while not buf_q.empty() or thr.active_count() != start_cnt: - try: - action, name, msg = buf_q.get(True, 0.25) - buf.write(action, name, ['OK'] if not msg else msg) - buf_q.task_done() - except queue.Empty: - pass - except KeyboardInterrupt: - G_STOP.set() - - if mac_gui: - rthread.stop() - rthread.join() - -main() -EOF -endfunction - -function! s:update_ruby() - ruby << EOF - module PlugStream - SEP = ["\r", "\n", nil] - def get_line - buffer = '' - loop do - char = readchar rescue return - if SEP.include? char.chr - buffer << $/ - break - else - buffer << char - end - end - buffer - end - end unless defined?(PlugStream) - - def esc arg - %["#{arg.gsub('"', '\"')}"] - end - - def killall pid - pids = [pid] - if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM - pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } - else - unless `which pgrep 2> /dev/null`.empty? - children = pids - until children.empty? - children = children.map { |pid| - `pgrep -P #{pid}`.lines.map { |l| l.chomp } - }.flatten - pids += children - end - end - pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } - end - end - - def compare_git_uri a, b - regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} - regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) - end - - require 'thread' - require 'fileutils' - require 'timeout' - running = true - iswin = VIM::evaluate('s:is_win').to_i == 1 - pull = VIM::evaluate('s:update.pull').to_i == 1 - base = VIM::evaluate('g:plug_home') - all = VIM::evaluate('s:update.todo') - limit = VIM::evaluate('get(g:, "plug_timeout", 60)') - tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 - nthr = VIM::evaluate('s:update.threads').to_i - maxy = VIM::evaluate('winheight(".")').to_i - vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ - cd = iswin ? 'cd /d' : 'cd' - tot = VIM::evaluate('len(s:update.todo)') || 0 - bar = '' - skip = 'Already installed' - mtx = Mutex.new - take1 = proc { mtx.synchronize { running && all.shift } } - logh = proc { - cnt = bar.length - $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" - $curbuf[2] = '[' + bar.ljust(tot) + ']' - VIM::command('normal! 2G') - VIM::command('redraw') - } - where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } - log = proc { |name, result, type| - mtx.synchronize do - ing = ![true, false].include?(type) - bar += type ? '=' : 'x' unless ing - b = case type - when :install then '+' when :update then '*' - when true, nil then '-' else - VIM::command("call add(s:update.errors, '#{name}')") - 'x' - end - result = - if type || type.nil? - ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] - elsif result =~ /^Interrupted|^Timeout/ - ["#{b} #{name}: #{result}"] - else - ["#{b} #{name}"] + result.lines.map { |l| " " << l } - end - if lnum = where.call(name) - $curbuf.delete lnum - lnum = 4 if ing && lnum > maxy - end - result.each_with_index do |line, offset| - $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) - end - logh.call - end - } - bt = proc { |cmd, name, type, cleanup| - tried = timeout = 0 - begin - tried += 1 - timeout += limit - fd = nil - data = '' - if iswin - Timeout::timeout(timeout) do - tmp = VIM::evaluate('tempname()') - system("(#{cmd}) > #{tmp}") - data = File.read(tmp).chomp - File.unlink tmp rescue nil - end - else - fd = IO.popen(cmd).extend(PlugStream) - first_line = true - log_prob = 1.0 / nthr - while line = Timeout::timeout(timeout) { fd.get_line } - data << line - log.call name, line.chomp, type if name && (first_line || rand < log_prob) - first_line = false - end - fd.close - end - [$? == 0, data.chomp] - rescue Timeout::Error, Interrupt => e - if fd && !fd.closed? - killall fd.pid - fd.close - end - cleanup.call if cleanup - if e.is_a?(Timeout::Error) && tried < tries - 3.downto(1) do |countdown| - s = countdown > 1 ? 's' : '' - log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type - sleep 1 - end - log.call name, 'Retrying ...', type - retry - end - [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] - end - } - main = Thread.current - threads = [] - watcher = Thread.new { - if vim7 - while VIM::evaluate('getchar(1)') - sleep 0.1 - end - else - require 'io/console' # >= Ruby 1.9 - nil until IO.console.getch == 3.chr - end - mtx.synchronize do - running = false - threads.each { |t| t.raise Interrupt } unless vim7 - end - threads.each { |t| t.join rescue nil } - main.kill - } - refresh = Thread.new { - while true - mtx.synchronize do - break unless running - VIM::command('noautocmd normal! a') - end - sleep 0.2 - end - } if VIM::evaluate('s:mac_gui') == 1 - - clone_opt = VIM::evaluate('s:clone_opt').join(' ') - progress = VIM::evaluate('s:progress_opt(1)') - nthr.times do - mtx.synchronize do - threads << Thread.new { - while pair = take1.call - name = pair.first - dir, uri, tag = pair.last.values_at *%w[dir uri tag] - exists = File.directory? dir - ok, result = - if exists - chdir = "#{cd} #{iswin ? dir : esc(dir)}" - ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil - current_uri = data.lines.to_a.last - if !ret - if data =~ /^Interrupted|^Timeout/ - [false, data] - else - [false, [data.chomp, "PlugClean required."].join($/)] - end - elsif !compare_git_uri(current_uri, uri) - [false, ["Invalid URI: #{current_uri}", - "Expected: #{uri}", - "PlugClean required."].join($/)] - else - if pull - log.call name, 'Updating ...', :update - fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' - bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil - else - [true, skip] - end - end - else - d = esc dir.sub(%r{[\\/]+$}, '') - log.call name, 'Installing ...', :install - bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { - FileUtils.rm_rf dir - } - end - mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok - log.call name, result, ok - end - } if running - end - end - threads.each { |t| t.join rescue nil } - logh.call - refresh.kill if refresh - watcher.kill -EOF -endfunction - -function! s:shellesc_cmd(arg, script) - let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') - return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') -endfunction - -function! s:shellesc_ps1(arg) - return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" -endfunction - -function! s:shellesc_sh(arg) - return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" -endfunction - -" Escape the shell argument based on the shell. -" Vim and Neovim's shellescape() are insufficient. -" 1. shellslash determines whether to use single/double quotes. -" Double-quote escaping is fragile for cmd.exe. -" 2. It does not work for powershell. -" 3. It does not work for *sh shells if the command is executed -" via cmd.exe (ie. cmd.exe /c sh -c command command_args) -" 4. It does not support batchfile syntax. -" -" Accepts an optional dictionary with the following keys: -" - shell: same as Vim/Neovim 'shell' option. -" If unset, fallback to 'cmd.exe' on Windows or 'sh'. -" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. -function! plug#shellescape(arg, ...) - if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' - return a:arg - endif - let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} - let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') - let script = get(opts, 'script', 1) - if shell =~# 'cmd\(\.exe\)\?$' - return s:shellesc_cmd(a:arg, script) - elseif s:is_powershell(shell) - return s:shellesc_ps1(a:arg) - endif - return s:shellesc_sh(a:arg) -endfunction - -function! s:glob_dir(path) - return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') -endfunction - -function! s:progress_bar(line, bar, total) - call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') -endfunction - -function! s:compare_git_uri(a, b) - " See `git help clone' - " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] - " [git@] github.com[:port] : junegunn/vim-plug [.git] - " file:// / junegunn/vim-plug [/] - " / junegunn/vim-plug [/] - let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' - let ma = matchlist(a:a, pat) - let mb = matchlist(a:b, pat) - return ma[1:2] ==# mb[1:2] -endfunction - -function! s:format_message(bullet, name, message) - if a:bullet != 'x' - return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] - else - let lines = map(s:lines(a:message), '" ".v:val') - return extend([printf('x %s:', a:name)], lines) - endif -endfunction - -function! s:with_cd(cmd, dir, ...) - let script = a:0 > 0 ? a:1 : 1 - return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) -endfunction - -function! s:system(cmd, ...) - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(1) - if type(a:cmd) == s:TYPE.list - " Neovim's system() supports list argument to bypass the shell - " but it cannot set the working directory for the command. - " Assume that the command does not rely on the shell. - if has('nvim') && a:0 == 0 - return system(a:cmd) - endif - let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) - if s:is_powershell(&shell) - let cmd = '& ' . cmd - endif - else - let cmd = a:cmd - endif - if a:0 > 0 - let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) - endif - if s:is_win && type(a:cmd) != s:TYPE.list - let [batchfile, cmd] = s:batchfile(cmd) - endif - return system(cmd) - finally - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry -endfunction - -function! s:system_chomp(...) - let ret = call('s:system', a:000) - return v:shell_error ? '' : substitute(ret, '\n$', '', '') -endfunction - -function! s:git_validate(spec, check_branch) - let err = '' - if isdirectory(a:spec.dir) - let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] - let remote = result[-1] - if empty(remote) - let err = join([remote, 'PlugClean required.'], "\n") - elseif !s:compare_git_uri(remote, a:spec.uri) - let err = join(['Invalid URI: '.remote, - \ 'Expected: '.a:spec.uri, - \ 'PlugClean required.'], "\n") - elseif a:check_branch && has_key(a:spec, 'commit') - let sha = s:git_revision(a:spec.dir) - if empty(sha) - let err = join(add(result, 'PlugClean required.'), "\n") - elseif !s:hash_match(sha, a:spec.commit) - let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', - \ a:spec.commit[:6], sha[:6]), - \ 'PlugUpdate required.'], "\n") - endif - elseif a:check_branch - let current_branch = result[0] - " Check tag - let origin_branch = s:git_origin_branch(a:spec) - if has_key(a:spec, 'tag') - let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) - if a:spec.tag !=# tag && a:spec.tag !~ '\*' - let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', - \ (empty(tag) ? 'N/A' : tag), a:spec.tag) - endif - " Check branch - elseif origin_branch !=# current_branch - let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', - \ current_branch, origin_branch) - endif - if empty(err) - let [ahead, behind] = split(s:lastline(s:system([ - \ 'git', 'rev-list', '--count', '--left-right', - \ printf('HEAD...origin/%s', origin_branch) - \ ], a:spec.dir)), '\t') - if !v:shell_error && ahead - if behind - " Only mention PlugClean if diverged, otherwise it's likely to be - " pushable (and probably not that messed up). - let err = printf( - \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" - \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) - else - let err = printf("Ahead of origin/%s by %d commit(s).\n" - \ .'Cannot update until local changes are pushed.', - \ origin_branch, ahead) - endif - endif - endif - endif - else - let err = 'Not found' - endif - return [err, err =~# 'PlugClean'] -endfunction - -function! s:rm_rf(dir) - if isdirectory(a:dir) - return s:system(s:is_win - \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) - \ : ['rm', '-rf', a:dir]) - endif -endfunction - -function! s:clean(force) - call s:prepare() - call append(0, 'Searching for invalid plugins in '.g:plug_home) - call append(1, '') - - " List of valid directories - let dirs = [] - let errs = {} - let [cnt, total] = [0, len(g:plugs)] - for [name, spec] in items(g:plugs) - if !s:is_managed(name) - call add(dirs, spec.dir) - else - let [err, clean] = s:git_validate(spec, 1) - if clean - let errs[spec.dir] = s:lines(err)[0] - else - call add(dirs, spec.dir) - endif - endif - let cnt += 1 - call s:progress_bar(2, repeat('=', cnt), total) - normal! 2G - redraw - endfor - - let allowed = {} - for dir in dirs - let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 - let allowed[dir] = 1 - for child in s:glob_dir(dir) - let allowed[child] = 1 - endfor - endfor - - let todo = [] - let found = sort(s:glob_dir(g:plug_home)) - while !empty(found) - let f = remove(found, 0) - if !has_key(allowed, f) && isdirectory(f) - call add(todo, f) - call append(line('$'), '- ' . f) - if has_key(errs, f) - call append(line('$'), ' ' . errs[f]) - endif - let found = filter(found, 'stridx(v:val, f) != 0') - end - endwhile - - 4 - redraw - if empty(todo) - call append(line('$'), 'Already clean.') - else - let s:clean_count = 0 - call append(3, ['Directories to delete:', '']) - redraw! - if a:force || s:ask_no_interrupt('Delete all directories?') - call s:delete([6, line('$')], 1) - else - call setline(4, 'Cancelled.') - nnoremap 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 - let err_count = 0 - while l1 <= l2 - let line = getline(l1) - if line =~ '^- ' && isdirectory(line[2:]) - execute l1 - redraw! - let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) - let force = force || answer > 1 - if answer - let err = s:rm_rf(line[2:]) - setlocal modifiable - if empty(err) - call setline(l1, '~'.line[1:]) - let s:clean_count += 1 - else - delete _ - call append(l1 - 1, s:format_message('x', line[1:], err)) - let l2 += len(s:lines(err)) - let err_count += 1 - endif - let msg = printf('Removed %d directories.', s:clean_count) - if err_count > 0 - let msg .= printf(' Failed to remove %d directories.', err_count) - endif - call setline(4, msg) - setlocal nomodifiable - endif - endif - let l1 += 1 - endwhile -endfunction - -function! s:upgrade() - echo 'Downloading the latest version of vim-plug' - redraw - let tmp = s:plug_tempname() - let new = tmp . '/plug.vim' - - try - let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) - if v:shell_error - return s:err('Error upgrading vim-plug: '. out) - endif - - if readfile(s:me) ==# readfile(new) - echo 'vim-plug is already up-to-date' - return 0 - else - call rename(s:me, s:me . '.old') - call rename(new, s:me) - unlet g:loaded_plug - echo 'vim-plug has been upgraded' - return 1 - endif - finally - silent! call s:rm_rf(tmp) - endtry -endfunction - -function! s:upgrade_specs() - for spec in values(g:plugs) - let spec.frozen = get(spec, 'frozen', 0) - endfor -endfunction - -function! s:status() - call s:prepare() - call append(0, 'Checking plugins') - call append(1, '') - - let ecnt = 0 - let unloaded = 0 - let [cnt, total] = [0, len(g:plugs)] - for [name, spec] in items(g:plugs) - let is_dir = isdirectory(spec.dir) - if has_key(spec, 'uri') - if is_dir - let [err, _] = s:git_validate(spec, 1) - let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] - else - let [valid, msg] = [0, 'Not found. Try PlugInstall.'] - endif - else - if is_dir - let [valid, msg] = [1, 'OK'] - else - let [valid, msg] = [0, 'Not found.'] - endif - endif - let cnt += 1 - let ecnt += !valid - " `s:loaded` entry can be missing if PlugUpgraded - if is_dir && get(s:loaded, name, -1) == 0 - let unloaded = 1 - let msg .= ' (not loaded)' - endif - call s:progress_bar(2, repeat('=', cnt), total) - call append(3, s:format_message(valid ? '-' : 'x', name, msg)) - normal! 2G - redraw - endfor - call setline(1, 'Finished. '.ecnt.' error(s).') - normal! gg - setlocal nomodifiable - if unloaded - echo "Press 'L' on each line to load plugin, or 'U' to update" - nnoremap 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 - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(1) - let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha - if s:is_win - let [batchfile, cmd] = s:batchfile(cmd) - endif - execute 'silent %!' cmd - finally - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry - setlocal nomodifiable - nnoremap 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 branch = s:git_origin_branch(v) - if len(branch) - let range = origin ? '..origin/'.branch : 'HEAD@{1}..' - let cmd = ['git', 'log', '--graph', '--color=never'] - if s:git_version_requirement(2, 10, 0) - call add(cmd, '--no-show-signature') - endif - call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) - if has_key(v, 'rtp') - call extend(cmd, ['--', v.rtp]) - endif - let diff = s:system_chomp(cmd, v.dir) - if !empty(diff) - let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' - call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) - let cnts[origin] += 1 - endif - endif - let bar .= '=' - call s:progress_bar(2, bar, len(total)) - normal! 2G - redraw - endfor - if !cnts[origin] - call append(5, ['', 'N/A']) - endif - endfor - call setline(1, printf('%d plugin(s) updated.', cnts[0]) - \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) - - if cnts[0] || cnts[1] - nnoremap (plug-preview) :silent! call preview_commit() - if empty(maparg("\", 'n')) - nmap (plug-preview) - endif - if empty(maparg('o', 'n')) - nmap o (plug-preview) - endif - endif - if cnts[0] - nnoremap X :call revert() - echo "Press 'X' on each block to revert the update" - endif - normal! gg - setlocal nomodifiable -endfunction - -function! s:revert() - if search('^Pending updates', 'bnW') - return - endif - - let name = s:find_name(line('.')) - if empty(name) || !has_key(g:plugs, name) || - \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' - return - endif - - call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) - setlocal modifiable - normal! "_dap - setlocal nomodifiable - echo 'Reverted' -endfunction - -function! s:snapshot(force, ...) abort - call s:prepare() - setf vim - call append(0, ['" Generated by vim-plug', - \ '" '.strftime("%c"), - \ '" :source this file in vim to restore the snapshot', - \ '" or execute: vim -S snapshot.vim', - \ '', '', 'PlugUpdate!']) - 1 - let anchor = line('$') - 3 - let names = sort(keys(filter(copy(g:plugs), - \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) - for name in reverse(names) - let sha = s:git_revision(g:plugs[name].dir) - if !empty(sha) - call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) - redraw - endif - endfor - - if a:0 > 0 - let fn = s:plug_expand(a:1) - if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) - return - endif - call writefile(getline(1, '$'), fn) - echo 'Saved as '.a:1 - silent execute 'e' s:esc(fn) - setf vim - endif -endfunction - -function! s:split_rtp() - return split(&rtp, '\\\@`-mappings | -"| `for` | On-demand loading: File types | -"| `frozen` | Do not update unless explicitly specified | -" -" More information: https://github.com/junegunn/vim-plug -" -" -" Copyright (c) 2017 Junegunn Choi -" -" MIT License -" -" Permission is hereby granted, free of charge, to any person obtaining -" a copy of this software and associated documentation files (the -" "Software"), to deal in the Software without restriction, including -" without limitation the rights to use, copy, modify, merge, publish, -" distribute, sublicense, and/or sell copies of the Software, and to -" permit persons to whom the Software is furnished to do so, subject to -" the following conditions: -" -" The above copyright notice and this permission notice shall be -" included in all copies or substantial portions of the Software. -" -" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -if exists('g:loaded_plug') - finish -endif -let g:loaded_plug = 1 - -let s:cpo_save = &cpo -set cpo&vim - -let s:plug_src = 'https://github.com/junegunn/vim-plug.git' -let s:plug_tab = get(s:, 'plug_tab', -1) -let s:plug_buf = get(s:, 'plug_buf', -1) -let s:mac_gui = has('gui_macvim') && has('gui_running') -let s:is_win = has('win32') -let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) -let s:vim8 = has('patch-8.0.0039') && exists('*job_start') -if s:is_win && &shellslash - set noshellslash - let s:me = resolve(expand(':p')) - set shellslash -else - let s:me = resolve(expand(':p')) -endif -let s:base_spec = { 'branch': '', 'frozen': 0 } -let s:TYPE = { -\ 'string': type(''), -\ 'list': type([]), -\ 'dict': type({}), -\ 'funcref': type(function('call')) -\ } -let s:loaded = get(s:, 'loaded', {}) -let s:triggers = get(s:, 'triggers', {}) - -function! s:isabsolute(dir) abort - return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') -endfunction - -function! s:git_dir(dir) abort - let gitdir = s:trim(a:dir) . '/.git' - if isdirectory(gitdir) - return gitdir - endif - if !filereadable(gitdir) - return '' - endif - let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') - if len(gitdir) && !s:isabsolute(gitdir) - let gitdir = a:dir . '/' . gitdir - endif - return isdirectory(gitdir) ? gitdir : '' -endfunction - -function! s:git_origin_url(dir) abort - let gitdir = s:git_dir(a:dir) - let config = gitdir . '/config' - if empty(gitdir) || !filereadable(config) - return '' - endif - return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') -endfunction - -function! s:git_revision(dir) abort - let gitdir = s:git_dir(a:dir) - let head = gitdir . '/HEAD' - if empty(gitdir) || !filereadable(head) - return '' - endif - - let line = get(readfile(head), 0, '') - let ref = matchstr(line, '^ref: \zs.*') - if empty(ref) - return line - endif - - if filereadable(gitdir . '/' . ref) - return get(readfile(gitdir . '/' . ref), 0, '') - endif - - if filereadable(gitdir . '/packed-refs') - for line in readfile(gitdir . '/packed-refs') - if line =~# ' ' . ref - return matchstr(line, '^[0-9a-f]*') - endif - endfor - endif - - return '' -endfunction - -function! s:git_local_branch(dir) abort - let gitdir = s:git_dir(a:dir) - let head = gitdir . '/HEAD' - if empty(gitdir) || !filereadable(head) - return '' - endif - let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') - return len(branch) ? branch : 'HEAD' -endfunction - -function! s:git_origin_branch(spec) - if len(a:spec.branch) - return a:spec.branch - endif - - " The file may not be present if this is a local repository - let gitdir = s:git_dir(a:spec.dir) - let origin_head = gitdir.'/refs/remotes/origin/HEAD' - if len(gitdir) && filereadable(origin_head) - return matchstr(get(readfile(origin_head), 0, ''), - \ '^ref: refs/remotes/origin/\zs.*') - endif - - " The command may not return the name of a branch in detached HEAD state - let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) - return v:shell_error ? '' : result[-1] -endfunction - -if s:is_win - function! s:plug_call(fn, ...) - let shellslash = &shellslash - try - set noshellslash - return call(a:fn, a:000) - finally - let &shellslash = shellslash - endtry - endfunction -else - function! s:plug_call(fn, ...) - return call(a:fn, a:000) - endfunction -endif - -function! s:plug_getcwd() - return s:plug_call('getcwd') -endfunction - -function! s:plug_fnamemodify(fname, mods) - return s:plug_call('fnamemodify', a:fname, a:mods) -endfunction - -function! s:plug_expand(fmt) - return s:plug_call('expand', a:fmt, 1) -endfunction - -function! s:plug_tempname() - return s:plug_call('tempname') -endfunction - -function! plug#begin(...) - if a:0 > 0 - let s:plug_home_org = a:1 - let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) - elseif exists('g:plug_home') - let home = s:path(g:plug_home) - elseif !empty(&rtp) - let home = s:path(split(&rtp, ',')[0]) . '/plugged' - else - return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') - endif - if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp - return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') - endif - - let g:plug_home = home - let g:plugs = {} - let g:plugs_order = [] - let s:triggers = {} - - call s:define_commands() - return 1 -endfunction - -function! s:define_commands() - command! -nargs=+ -bar Plug call plug#() - if !executable('git') - return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') - endif - if has('win32') - \ && &shellslash - \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$') - return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') - endif - if !has('nvim') - \ && (has('win32') || has('win32unix')) - \ && !has('multi_byte') - return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') - endif - command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(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! s:lazy(plug, opt) - return has_key(a:plug, a:opt) && - \ (empty(s:to_a(a:plug[a:opt])) || - \ !isdirectory(a:plug.dir) || - \ len(s:glob(s:rtp(a:plug), 'plugin')) || - \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) -endfunction - -function! plug#end() - if !exists('g:plugs') - return s:err('plug#end() called without calling plug#begin() first') - endif - - if exists('#PlugLOD') - augroup PlugLOD - autocmd! - augroup END - augroup! PlugLOD - endif - let lod = { 'ft': {}, 'map': {}, 'cmd': {} } - - if exists('g:did_load_filetypes') - filetype off - endif - for name in g:plugs_order - if !has_key(g:plugs, name) - continue - endif - let plug = g:plugs[name] - if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') - let s:loaded[name] = 1 - continue - endif - - if has_key(plug, 'on') - let s:triggers[name] = { 'map': [], 'cmd': [] } - for cmd in s:to_a(plug.on) - if cmd =~? '^.\+' - 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 - -function! s:rtp(spec) - return s:path(a:spec.dir . get(a:spec, 'rtp', '')) -endfunction - -if s:is_win - function! s:path(path) - return s:trim(substitute(a:path, '/', '\', 'g')) - endfunction - - function! s:dirpath(path) - return s:path(a:path) . '\' - endfunction - - function! s:is_local_plug(repo) - return a:repo =~? '^[a-z]:\|^[%~]' - endfunction - - " Copied from fzf - function! s:wrap_cmds(cmds) - let cmds = [ - \ '@echo off', - \ 'setlocal enabledelayedexpansion'] - \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) - \ + ['endlocal'] - if has('iconv') - if !exists('s:codepage') - let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) - endif - return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) - endif - return map(cmds, 'v:val."\r"') - endfunction - - function! s:batchfile(cmd) - let batchfile = s:plug_tempname().'.bat' - call writefile(s:wrap_cmds(a:cmd), batchfile) - let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) - if &shell =~# 'powershell\(\.exe\)\?$' - let cmd = '& ' . cmd - endif - return [batchfile, cmd] - endfunction -else - function! s:path(path) - return s:trim(a:path) - endfunction - - function! s:dirpath(path) - return substitute(a:path, '[/\\]*$', '/', '') - endfunction - - function! s:is_local_plug(repo) - return a:repo[0] =~ '[/$~]' - endfunction -endif - -function! s:err(msg) - echohl ErrorMsg - echom '[vim-plug] '.a:msg - echohl None -endfunction - -function! s:warn(cmd, msg) - echohl WarningMsg - execute a:cmd 'a:msg' - echohl None -endfunction - -function! s:esc(path) - return escape(a:path, ' ') -endfunction - -function! s:escrtp(path) - return escape(a:path, ' ,') -endfunction - -function! s:remove_rtp() - for name in s:loaded_names() - let rtp = s:rtp(g:plugs[name]) - execute 'set rtp-='.s:escrtp(rtp) - let after = globpath(rtp, 'after') - if isdirectory(after) - execute 'set rtp-='.s:escrtp(after) - endif - endfor -endfunction - -function! s:reorg_rtp() - if !empty(s:first_rtp) - execute 'set rtp-='.s:first_rtp - execute 'set rtp-='.s:last_rtp - endif - - " &rtp is modified from outside - if exists('s:prtp') && s:prtp !=# &rtp - call s:remove_rtp() - unlet! s:middle - endif - - let s:middle = get(s:, 'middle', &rtp) - let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') - let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') - let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') - \ . ','.s:middle.',' - \ . join(map(afters, 'escape(v:val, ",")'), ',') - let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') - let s:prtp = &rtp - - if !empty(s:first_rtp) - execute 'set rtp^='.s:first_rtp - execute 'set rtp+='.s:last_rtp - endif -endfunction - -function! s:doautocmd(...) - if exists('#'.join(a:000, '#')) - execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) - endif -endfunction - -function! s:dobufread(names) - for name in a:names - let path = s:rtp(g:plugs[name]) - for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] - if len(finddir(dir, path)) - if exists('#BufRead') - doautocmd BufRead - endif - return - endif - endfor - endfor -endfunction - -function! plug#load(...) - if a:0 == 0 - return s:err('Argument missing: plugin name(s) required') - endif - if !exists('g:plugs') - return s:err('plug#begin was not called') - endif - let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 - let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') - if !empty(unknowns) - let s = len(unknowns) > 1 ? 's' : '' - return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) - end - let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') - if !empty(unloaded) - for name in unloaded - call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - endfor - call s:dobufread(unloaded) - return 1 - end - return 0 -endfunction - -function! s:remove_triggers(name) - if !has_key(s:triggers, a:name) - return - endif - for cmd in s:triggers[a:name].cmd - execute 'silent! delc' cmd - endfor - for map in s:triggers[a:name].map - execute 'silent! unmap' map - execute 'silent! iunmap' map - endfor - call remove(s:triggers, a:name) -endfunction - -function! s:lod(names, types, ...) - for name in a:names - call s:remove_triggers(name) - let s:loaded[name] = 1 - endfor - call s:reorg_rtp() - - for name in a:names - let rtp = s:rtp(g:plugs[name]) - for dir in a:types - call s:source(rtp, dir.'/**/*.vim') - endfor - if a:0 - if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) - execute 'runtime' a:1 - endif - call s:source(rtp, a:2) - endif - call s:doautocmd('User', name) - endfor -endfunction - -function! s:lod_ft(pat, names) - let syn = 'syntax/'.a:pat.'.vim' - call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) - execute 'autocmd! PlugLOD FileType' a:pat - call s:doautocmd('filetypeplugin', 'FileType') - call s:doautocmd('filetypeindent', 'FileType') -endfunction - -function! s:lod_cmd(cmd, bang, l1, l2, args, names) - call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - call s:dobufread(a:names) - execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) -endfunction - -function! s:lod_map(map, names, with_prefix, prefix) - call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) - call s:dobufread(a:names) - let extra = '' - while 1 - let c = getchar(0) - if c == 0 - break - endif - let extra .= nr2char(c) - endwhile - - if a:with_prefix - let prefix = v:count ? v:count : '' - let prefix .= '"'.v:register.a:prefix - if mode(1) == 'no' - if v:operator == 'c' - let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) - let spec = extend(s:infer_properties(name, repo), opts) - if !has_key(g:plugs, name) - call add(g:plugs_order, name) - endif - let g:plugs[name] = spec - let s:loaded[name] = get(s:loaded, name, 0) - catch - return s:err(repo . ' ' . v:exception) - endtry -endfunction - -function! s:parse_options(arg) - let opts = copy(s:base_spec) - let type = type(a:arg) - let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' - if type == s:TYPE.string - if empty(a:arg) - throw printf(opt_errfmt, 'tag', 'string') - endif - let opts.tag = a:arg - elseif type == s:TYPE.dict - for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] - if has_key(a:arg, opt) - \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) - throw printf(opt_errfmt, opt, 'string') - endif - endfor - for opt in ['on', 'for'] - if has_key(a:arg, opt) - \ && type(a:arg[opt]) != s:TYPE.list - \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) - throw printf(opt_errfmt, opt, 'string or list') - endif - endfor - if has_key(a:arg, 'do') - \ && type(a:arg.do) != s:TYPE.funcref - \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) - throw printf(opt_errfmt, 'do', 'string or funcref') - endif - call extend(opts, a:arg) - if has_key(opts, 'dir') - let opts.dir = s:dirpath(s:plug_expand(opts.dir)) - endif - else - throw 'Invalid argument type (expected: string or dictionary)' - endif - return opts -endfunction - -function! s:infer_properties(name, repo) - let repo = a:repo - if s:is_local_plug(repo) - return { 'dir': s:dirpath(s:plug_expand(repo)) } - else - if repo =~ ':' - let uri = repo - else - if repo !~ '/' - throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) - endif - let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') - let uri = printf(fmt, repo) - endif - return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } - endif -endfunction - -function! s:install(force, names) - call s:update_impl(0, a:force, a:names) -endfunction - -function! s:update(force, names) - call s:update_impl(1, a:force, a:names) -endfunction - -function! plug#helptags() - if !exists('g:plugs') - return s:err('plug#begin was not called') - endif - for spec in values(g:plugs) - let docd = join([s:rtp(spec), 'doc'], '/') - if isdirectory(docd) - silent! execute 'helptags' s:esc(docd) - endif - endfor - return 1 -endfunction - -function! s:syntax() - syntax clear - syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber - syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX - syn match plugNumber /[0-9]\+[0-9.]*/ contained - syn match plugBracket /[[\]]/ contained - syn match plugX /x/ contained - syn match plugDash /^-\{1}\ / - syn match plugPlus /^+/ - syn match plugStar /^*/ - syn match plugMessage /\(^- \)\@<=.*/ - syn match plugName /\(^- \)\@<=[^ ]*:/ - syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ - syn match plugTag /(tag: [^)]\+)/ - syn match plugInstall /\(^+ \)\@<=[^:]*/ - syn match plugUpdate /\(^* \)\@<=[^:]*/ - syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag - syn match plugEdge /^ \X\+$/ - syn match plugEdge /^ \X*/ contained nextgroup=plugSha - syn match plugSha /[0-9a-f]\{7,9}/ contained - syn match plugRelDate /([^)]*)$/ contained - syn match plugNotLoaded /(not loaded)$/ - syn match plugError /^x.*/ - syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ - syn match plugH2 /^.*:\n-\+$/ - syn match plugH2 /^-\{2,}/ - syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean - hi def link plug1 Title - hi def link plug2 Repeat - hi def link plugH2 Type - hi def link plugX Exception - hi def link plugBracket Structure - hi def link plugNumber Number - - hi def link plugDash Special - hi def link plugPlus Constant - hi def link plugStar Boolean - - hi def link plugMessage Function - hi def link plugName Label - hi def link plugInstall Function - hi def link plugUpdate Type - - hi def link plugError Error - hi def link plugDeleted Ignore - hi def link plugRelDate Comment - hi def link plugEdge PreProc - hi def link plugSha Identifier - hi def link plugTag Constant - - hi def link plugNotLoaded Comment -endfunction - -function! s:lpad(str, len) - return a:str . repeat(' ', a:len - len(a:str)) -endfunction - -function! s:lines(msg) - return split(a:msg, "[\r\n]") -endfunction - -function! s:lastline(msg) - return get(s:lines(a:msg), -1, '') -endfunction - -function! s:new_window() - execute get(g:, 'plug_window', 'vertical topleft new') -endfunction - -function! s:plug_window_exists() - let buflist = tabpagebuflist(s:plug_tab) - return !empty(buflist) && index(buflist, s:plug_buf) >= 0 -endfunction - -function! s:switch_in() - if !s:plug_window_exists() - return 0 - endif - - if winbufnr(0) != s:plug_buf - let s:pos = [tabpagenr(), winnr(), winsaveview()] - execute 'normal!' s:plug_tab.'gt' - let winnr = bufwinnr(s:plug_buf) - execute winnr.'wincmd w' - call add(s:pos, winsaveview()) - else - let s:pos = [winsaveview()] - endif - - setlocal modifiable - return 1 -endfunction - -function! s:switch_out(...) - call winrestview(s:pos[-1]) - setlocal nomodifiable - if a:0 > 0 - execute a:1 - endif - - if len(s:pos) > 1 - execute 'normal!' s:pos[0].'gt' - execute s:pos[1] 'wincmd w' - call winrestview(s:pos[2]) - endif -endfunction - -function! s:finish_bindings() - nnoremap 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(s:plug_getcwd()) - throw 'Invalid current working directory. Cannot proceed.' - endif - - for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] - if exists(evar) - throw evar.' detected. Cannot proceed.' - endif - endfor - - call s:job_abort() - if s:switch_in() - if b:plug_preview == 1 - pc - endif - enew - else - call s:new_window() - endif - - nnoremap q :call close_pane() - 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 - if exists('+colorcolumn') - setlocal colorcolumn= - endif - setf vim-plug - if exists('g:syntax_on') - call s:syntax() - endif -endfunction - -function! s:close_pane() - if b:plug_preview == 1 - pc - let b:plug_preview = -1 - else - bd - endif -endfunction - -function! s:assign_name() - " Assign buffer name - let prefix = '[Plugins]' - let name = prefix - let idx = 2 - while bufexists(name) - let name = printf('%s (%s)', prefix, idx) - let idx = idx + 1 - endwhile - silent! execute 'f' fnameescape(name) -endfunction - -function! s:chsh(swap) - let prev = [&shell, &shellcmdflag, &shellredir] - if !s:is_win - set shell=sh - endif - if a:swap - if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$' - let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' - elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' - set shellredir=>%s\ 2>&1 - endif - endif - return prev -endfunction - -function! s:bang(cmd, ...) - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(a:0) - " FIXME: Escaping is incomplete. We could use shellescape with eval, - " but it won't work on Windows. - let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd - if s:is_win - let [batchfile, cmd] = s:batchfile(cmd) - endif - let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') - execute "normal! :execute g:_plug_bang\\" - finally - unlet g:_plug_bang - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry - return v:shell_error ? 'Exit status: ' . v:shell_error : '' -endfunction - -function! s:regress_bar() - let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') - call s:progress_bar(2, bar, len(bar)) -endfunction - -function! s:is_updated(dir) - return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) -endfunction - -function! s:do(pull, force, todo) - for [name, spec] in items(a:todo) - if !isdirectory(spec.dir) - continue - endif - let installed = has_key(s:update.new, name) - let updated = installed ? 0 : - \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) - if a:force || installed || updated - execute 'cd' s:esc(spec.dir) - call append(3, '- Post-update hook for '. name .' ... ') - let error = '' - let type = type(spec.do) - if type == s:TYPE.string - if spec.do[0] == ':' - if !get(s:loaded, name, 0) - let s:loaded[name] = 1 - call s:reorg_rtp() - endif - call s:load_plugin(spec) - try - execute spec.do[1:] - catch - let error = v:exception - endtry - if !s:plug_window_exists() - cd - - throw 'Warning: vim-plug was terminated by the post-update hook of '.name - endif - else - let error = s:bang(spec.do) - endif - elseif type == s:TYPE.funcref - try - call s:load_plugin(spec) - let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') - call spec.do({ 'name': name, 'status': status, 'force': a:force }) - catch - let error = v:exception - endtry - else - let error = 'Invalid hook type' - endif - call s:switch_in() - call setline(4, empty(error) ? (getline(4) . 'OK') - \ : ('x' . getline(4)[1:] . error)) - if !empty(error) - call add(s:update.errors, name) - call s:regress_bar() - endif - cd - - endif - endfor -endfunction - -function! s:hash_match(a, b) - return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 -endfunction - -function! s:checkout(spec) - let sha = a:spec.commit - let output = s:git_revision(a:spec.dir) - if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) - let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' - let output = s:system( - \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) - endif - return output -endfunction - -function! s:finish(pull) - let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) - if new_frozen - let s = new_frozen > 1 ? 's' : '' - call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) - endif - call append(3, '- Finishing ... ') | 4 - redraw - call plug#helptags() - call plug#end() - call setline(4, getline(4) . 'Done!') - redraw - let msgs = [] - if !empty(s:update.errors) - call add(msgs, "Press 'R' to retry.") - endif - if a:pull && len(s:update.new) < len(filter(getline(5, '$'), - \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) - call add(msgs, "Press 'D' to see the updated changes.") - endif - echo join(msgs, ' ') - call s:finish_bindings() -endfunction - -function! s:retry() - if empty(s:update.errors) - return - endif - echo - call s:update_impl(s:update.pull, s:update.force, - \ extend(copy(s:update.errors), [s:update.threads])) -endfunction - -function! s:is_managed(name) - return has_key(g:plugs[a:name], 'uri') -endfunction - -function! s:names(...) - return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) -endfunction - -function! s:check_ruby() - silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") - if !exists('g:plug_ruby') - redraw! - return s:warn('echom', 'Warning: Ruby interface is broken') - endif - let ruby_version = split(g:plug_ruby, '\.') - unlet g:plug_ruby - return s:version_requirement(ruby_version, [1, 8, 7]) -endfunction - -function! s:update_impl(pull, force, args) abort - let sync = index(a:args, '--sync') >= 0 || has('vim_starting') - let args = filter(copy(a:args), 'v:val != "--sync"') - let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? - \ remove(args, -1) : get(g:, 'plug_threads', 16) - - let managed = filter(copy(g:plugs), 's:is_managed(v:key)') - let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : - \ filter(managed, 'index(args, v:key) >= 0') - - if empty(todo) - return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) - endif - - if !s:is_win && s:git_version_requirement(2, 3) - let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' - let $GIT_TERMINAL_PROMPT = 0 - for plug in values(todo) - let plug.uri = substitute(plug.uri, - \ '^https://git::@github\.com', 'https://github.com', '') - endfor - endif - - if !isdirectory(g:plug_home) - try - call mkdir(g:plug_home, 'p') - catch - return s:err(printf('Invalid plug directory: %s. '. - \ 'Try to call plug#begin with a valid directory', g:plug_home)) - endtry - endif - - if has('nvim') && !exists('*jobwait') && threads > 1 - call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') - endif - - let use_job = s:nvim || s:vim8 - let python = (has('python') || has('python3')) && !use_job - let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() - - let s:update = { - \ 'start': reltime(), - \ 'all': todo, - \ 'todo': copy(todo), - \ 'errors': [], - \ 'pull': a:pull, - \ 'force': a:force, - \ 'new': {}, - \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, - \ 'bar': '', - \ 'fin': 0 - \ } - - call s:prepare(1) - call append(0, ['', '']) - normal! 2G - silent! redraw - - let s:clone_opt = [] - if get(g:, 'plug_shallow', 1) - call extend(s:clone_opt, ['--depth', '1']) - if s:git_version_requirement(1, 7, 10) - call add(s:clone_opt, '--no-single-branch') - endif - endif - - if has('win32unix') || has('wsl') - call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) - endif - - let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' - - " Python version requirement (>= 2.7) - if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 - redir => pyv - silent python import platform; print platform.python_version() - redir END - let python = s:version_requirement( - \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) - endif - - if (python || ruby) && s:update.threads > 1 - try - let imd = &imd - if s:mac_gui - set noimd - endif - if ruby - call s:update_ruby() - else - call s:update_python() - endif - catch - let lines = getline(4, '$') - let printed = {} - silent! 4,$d _ - for line in lines - let name = s:extract_name(line, '.', '') - if empty(name) || !has_key(printed, name) - call append('$', line) - if !empty(name) - let printed[name] = 1 - if line[0] == 'x' && index(s:update.errors, name) < 0 - call add(s:update.errors, name) - end - endif - endif - endfor - finally - let &imd = imd - call s:update_finish() - endtry - else - call s:update_vim() - while use_job && sync - sleep 100m - if s:update.fin - break - endif - endwhile - endif -endfunction - -function! s:log4(name, msg) - call setline(4, printf('- %s (%s)', a:msg, a:name)) - redraw -endfunction - -function! s:update_finish() - if exists('s:git_terminal_prompt') - let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt - endif - if s:switch_in() - call append(3, '- Updating ...') | 4 - for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) - let [pos, _] = s:logpos(name) - if !pos - continue - endif - if has_key(spec, 'commit') - call s:log4(name, 'Checking out '.spec.commit) - let out = s:checkout(spec) - elseif has_key(spec, 'tag') - let tag = spec.tag - if tag =~ '\*' - let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) - if !v:shell_error && !empty(tags) - let tag = tags[0] - call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) - call append(3, '') - endif - endif - call s:log4(name, 'Checking out '.tag) - let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) - else - let branch = s:git_origin_branch(spec) - call s:log4(name, 'Merging origin/'.s:esc(branch)) - let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' - \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) - endif - if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && - \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) - call s:log4(name, 'Updating submodules. This may take a while.') - let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) - endif - let msg = s:format_message(v:shell_error ? 'x': '-', name, out) - if v:shell_error - call add(s:update.errors, name) - call s:regress_bar() - silent execute pos 'd _' - call append(4, msg) | 4 - elseif !empty(out) - call setline(pos, msg[0]) - endif - redraw - endfor - silent 4 d _ - try - call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) - catch - call s:warn('echom', v:exception) - call s:warn('echo', '') - return - endtry - call s:finish(s:update.pull) - call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') - call s:switch_out('normal! gg') - endif -endfunction - -function! s:job_abort() - if (!s:nvim && !s:vim8) || !exists('s:jobs') - return - endif - - for [name, j] in items(s:jobs) - if s:nvim - silent! call jobstop(j.jobid) - elseif s:vim8 - silent! call job_stop(j.jobid) - endif - if j.new - call s:rm_rf(g:plugs[name].dir) - endif - endfor - let s:jobs = {} -endfunction - -function! s:last_non_empty_line(lines) - let len = len(a:lines) - for idx in range(len) - let line = a:lines[len-idx-1] - if !empty(line) - return line - endif - endfor - return '' -endfunction - -function! s:job_out_cb(self, data) abort - let self = a:self - let data = remove(self.lines, -1) . a:data - let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') - call extend(self.lines, lines) - " To reduce the number of buffer updates - let self.tick = get(self, 'tick', -1) + 1 - if !self.running || self.tick % len(s:jobs) == 0 - let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') - let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) - call s:log(bullet, self.name, result) - endif -endfunction - -function! s:job_exit_cb(self, data) abort - let a:self.running = 0 - let a:self.error = a:data != 0 - call s:reap(a:self.name) - call s:tick() -endfunction - -function! s:job_cb(fn, job, ch, data) - if !s:plug_window_exists() " plug window closed - return s:job_abort() - endif - call call(a:fn, [a:job, a:data]) -endfunction - -function! s:nvim_cb(job_id, data, event) dict abort - return (a:event == 'stdout' || a:event == 'stderr') ? - \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : - \ s:job_cb('s:job_exit_cb', self, 0, a:data) -endfunction - -function! s:spawn(name, cmd, opts) - let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], - \ 'new': get(a:opts, 'new', 0) } - let s:jobs[a:name] = job - - if s:nvim - if has_key(a:opts, 'dir') - let job.cwd = a:opts.dir - endif - let argv = a:cmd - call extend(job, { - \ 'on_stdout': function('s:nvim_cb'), - \ 'on_stderr': function('s:nvim_cb'), - \ 'on_exit': function('s:nvim_cb'), - \ }) - let jid = s:plug_call('jobstart', argv, job) - if jid > 0 - let job.jobid = jid - else - let job.running = 0 - let job.error = 1 - let job.lines = [jid < 0 ? argv[0].' is not executable' : - \ 'Invalid arguments (or job table is full)'] - endif - elseif s:vim8 - let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) - if has_key(a:opts, 'dir') - let cmd = s:with_cd(cmd, a:opts.dir, 0) - endif - let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] - let jid = job_start(s:is_win ? join(argv, ' ') : argv, { - \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), - \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), - \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), - \ 'err_mode': 'raw', - \ 'out_mode': 'raw' - \}) - if job_status(jid) == 'run' - let job.jobid = jid - else - let job.running = 0 - let job.error = 1 - let job.lines = ['Failed to start job'] - endif - else - let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) - let job.error = v:shell_error != 0 - let job.running = 0 - endif -endfunction - -function! s:reap(name) - let job = s:jobs[a:name] - if job.error - call add(s:update.errors, a:name) - elseif get(job, 'new', 0) - let s:update.new[a:name] = 1 - endif - let s:update.bar .= job.error ? 'x' : '=' - - let bullet = job.error ? 'x' : '-' - let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) - call s:log(bullet, a:name, empty(result) ? 'OK' : result) - call s:bar() - - call remove(s:jobs, a:name) -endfunction - -function! s:bar() - if s:switch_in() - let total = len(s:update.all) - call setline(1, (s:update.pull ? 'Updating' : 'Installing'). - \ ' plugins ('.len(s:update.bar).'/'.total.')') - call s:progress_bar(2, s:update.bar, total) - call s:switch_out() - endif -endfunction - -function! s:logpos(name) - let max = line('$') - for i in range(4, max > 4 ? max : 4) - if getline(i) =~# '^[-+x*] '.a:name.':' - for j in range(i + 1, max > 5 ? max : 5) - if getline(j) !~ '^ ' - return [i, j - 1] - endif - endfor - return [i, i] - endif - endfor - return [0, 0] -endfunction - -function! s:log(bullet, name, lines) - if s:switch_in() - let [b, e] = s:logpos(a:name) - if b > 0 - silent execute printf('%d,%d d _', b, e) - if b > winheight('.') - let b = 4 - endif - else - let b = 4 - endif - " FIXME For some reason, nomodifiable is set after :d in vim8 - setlocal modifiable - call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) - call s:switch_out() - endif -endfunction - -function! s:update_vim() - let s:jobs = {} - - call s:bar() - call s:tick() -endfunction - -function! s:tick() - let pull = s:update.pull - let prog = s:progress_opt(s:nvim || s:vim8) -while 1 " Without TCO, Vim stack is bound to explode - if empty(s:update.todo) - if empty(s:jobs) && !s:update.fin - call s:update_finish() - let s:update.fin = 1 - endif - return - endif - - let name = keys(s:update.todo)[0] - let spec = remove(s:update.todo, name) - let new = empty(globpath(spec.dir, '.git', 1)) - - call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') - redraw - - let has_tag = has_key(spec, 'tag') - if !new - let [error, _] = s:git_validate(spec, 0) - if empty(error) - if pull - let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] - if has_tag && !empty(globpath(spec.dir, '.git/shallow')) - call extend(cmd, ['--depth', '99999999']) - endif - if !empty(prog) - call add(cmd, prog) - endif - call s:spawn(name, cmd, { 'dir': spec.dir }) - else - let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } - endif - else - let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } - endif - else - let cmd = ['git', 'clone'] - if !has_tag - call extend(cmd, s:clone_opt) - endif - if !empty(prog) - call add(cmd, prog) - endif - call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) - endif - - if !s:jobs[name].running - call s:reap(name) - endif - if len(s:jobs) >= s:update.threads - break - endif -endwhile -endfunction - -function! s:update_python() -let py_exe = has('python') ? 'python' : 'python3' -execute py_exe "<< EOF" -import datetime -import functools -import os -try: - import queue -except ImportError: - import Queue as queue -import random -import re -import shutil -import signal -import subprocess -import tempfile -import threading as thr -import time -import traceback -import vim - -G_NVIM = vim.eval("has('nvim')") == '1' -G_PULL = vim.eval('s:update.pull') == '1' -G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 -G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) -G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) -G_PROGRESS = vim.eval('s:progress_opt(1)') -G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) -G_STOP = thr.Event() -G_IS_WIN = vim.eval('s:is_win') == '1' - -class PlugError(Exception): - def __init__(self, msg): - self.msg = msg -class CmdTimedOut(PlugError): - pass -class CmdFailed(PlugError): - pass -class InvalidURI(PlugError): - pass -class Action(object): - INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] - -class Buffer(object): - def __init__(self, lock, num_plugs, is_pull): - self.bar = '' - self.event = 'Updating' if is_pull else 'Installing' - self.lock = lock - self.maxy = int(vim.eval('winheight(".")')) - self.num_plugs = num_plugs - - def __where(self, name): - """ Find first line with name in current buffer. Return line num. """ - found, lnum = False, 0 - matcher = re.compile('^[-+x*] {0}:'.format(name)) - for line in vim.current.buffer: - if matcher.search(line) is not None: - found = True - break - lnum += 1 - - if not found: - lnum = -1 - return lnum - - def header(self): - curbuf = vim.current.buffer - curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) - - num_spaces = self.num_plugs - len(self.bar) - curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') - - with self.lock: - vim.command('normal! 2G') - vim.command('redraw') - - def write(self, action, name, lines): - first, rest = lines[0], lines[1:] - msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] - msg.extend([' ' + line for line in rest]) - - try: - if action == Action.ERROR: - self.bar += 'x' - vim.command("call add(s:update.errors, '{0}')".format(name)) - elif action == Action.DONE: - self.bar += '=' - - curbuf = vim.current.buffer - lnum = self.__where(name) - if lnum != -1: # Found matching line num - del curbuf[lnum] - if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): - lnum = 3 - else: - lnum = 3 - curbuf.append(msg, lnum) - - self.header() - except vim.error: - pass - -class Command(object): - CD = 'cd /d' if G_IS_WIN else 'cd' - - def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): - self.cmd = cmd - if cmd_dir: - self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) - self.timeout = timeout - self.callback = cb if cb else (lambda msg: None) - self.clean = clean if clean else (lambda: None) - self.proc = None - - @property - def alive(self): - """ Returns true only if command still running. """ - return self.proc and self.proc.poll() is None - - def execute(self, ntries=3): - """ Execute the command with ntries if CmdTimedOut. - Returns the output of the command if no Exception. - """ - attempt, finished, limit = 0, False, self.timeout - - while not finished: - try: - attempt += 1 - result = self.try_command() - finished = True - return result - except CmdTimedOut: - if attempt != ntries: - self.notify_retry() - self.timeout += limit - else: - raise - - def notify_retry(self): - """ Retry required for command, notify user. """ - for count in range(3, 0, -1): - if G_STOP.is_set(): - raise KeyboardInterrupt - msg = 'Timeout. Will retry in {0} second{1} ...'.format( - count, 's' if count != 1 else '') - self.callback([msg]) - time.sleep(1) - self.callback(['Retrying ...']) - - def try_command(self): - """ Execute a cmd & poll for callback. Returns list of output. - Raises CmdFailed -> return code for Popen isn't 0 - Raises CmdTimedOut -> command exceeded timeout without new output - """ - first_line = True - - try: - tfile = tempfile.NamedTemporaryFile(mode='w+b') - preexec_fn = not G_IS_WIN and os.setsid or None - self.proc = subprocess.Popen(self.cmd, stdout=tfile, - stderr=subprocess.STDOUT, - stdin=subprocess.PIPE, shell=True, - preexec_fn=preexec_fn) - thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) - thrd.start() - - thread_not_started = True - while thread_not_started: - try: - thrd.join(0.1) - thread_not_started = False - except RuntimeError: - pass - - while self.alive: - if G_STOP.is_set(): - raise KeyboardInterrupt - - if first_line or random.random() < G_LOG_PROB: - first_line = False - line = '' if G_IS_WIN else nonblock_read(tfile.name) - if line: - self.callback([line]) - - time_diff = time.time() - os.path.getmtime(tfile.name) - if time_diff > self.timeout: - raise CmdTimedOut(['Timeout!']) - - thrd.join(0.5) - - tfile.seek(0) - result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] - - if self.proc.returncode != 0: - raise CmdFailed([''] + result) - - return result - except: - self.terminate() - raise - - def terminate(self): - """ Terminate process and cleanup. """ - if self.alive: - if G_IS_WIN: - os.kill(self.proc.pid, signal.SIGINT) - else: - os.killpg(self.proc.pid, signal.SIGTERM) - self.clean() - -class Plugin(object): - def __init__(self, name, args, buf_q, lock): - self.name = name - self.args = args - self.buf_q = buf_q - self.lock = lock - self.tag = args.get('tag', 0) - - def manage(self): - try: - if os.path.exists(self.args['dir']): - self.update() - else: - self.install() - with self.lock: - thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) - except PlugError as exc: - self.write(Action.ERROR, self.name, exc.msg) - except KeyboardInterrupt: - G_STOP.set() - self.write(Action.ERROR, self.name, ['Interrupted!']) - except: - # Any exception except those above print stack trace - msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) - self.write(Action.ERROR, self.name, msg.split('\n')) - raise - - def install(self): - target = self.args['dir'] - if target[-1] == '\\': - target = target[0:-1] - - def clean(target): - def _clean(): - try: - shutil.rmtree(target) - except OSError: - pass - return _clean - - self.write(Action.INSTALL, self.name, ['Installing ...']) - callback = functools.partial(self.write, Action.INSTALL, self.name) - cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( - '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], - esc(target)) - com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) - result = com.execute(G_RETRIES) - self.write(Action.DONE, self.name, result[-1:]) - - def repo_uri(self): - cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' - command = Command(cmd, self.args['dir'], G_TIMEOUT,) - result = command.execute(G_RETRIES) - return result[-1] - - def update(self): - actual_uri = self.repo_uri() - expect_uri = self.args['uri'] - regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') - ma = regex.match(actual_uri) - mb = regex.match(expect_uri) - if ma is None or mb is None or ma.groups() != mb.groups(): - msg = ['', - 'Invalid URI: {0}'.format(actual_uri), - 'Expected {0}'.format(expect_uri), - 'PlugClean required.'] - raise InvalidURI(msg) - - if G_PULL: - self.write(Action.UPDATE, self.name, ['Updating ...']) - callback = functools.partial(self.write, Action.UPDATE, self.name) - fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' - cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) - com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) - result = com.execute(G_RETRIES) - self.write(Action.DONE, self.name, result[-1:]) - else: - self.write(Action.DONE, self.name, ['Already installed']) - - def write(self, action, name, msg): - self.buf_q.put((action, name, msg)) - -class PlugThread(thr.Thread): - def __init__(self, tname, args): - super(PlugThread, self).__init__() - self.tname = tname - self.args = args - - def run(self): - thr.current_thread().name = self.tname - buf_q, work_q, lock = self.args - - try: - while not G_STOP.is_set(): - name, args = work_q.get_nowait() - plug = Plugin(name, args, buf_q, lock) - plug.manage() - work_q.task_done() - except queue.Empty: - pass - -class RefreshThread(thr.Thread): - def __init__(self, lock): - super(RefreshThread, self).__init__() - self.lock = lock - self.running = True - - def run(self): - while self.running: - with self.lock: - thread_vim_command('noautocmd normal! a') - time.sleep(0.33) - - def stop(self): - self.running = False - -if G_NVIM: - def thread_vim_command(cmd): - vim.session.threadsafe_call(lambda: vim.command(cmd)) -else: - def thread_vim_command(cmd): - vim.command(cmd) - -def esc(name): - return '"' + name.replace('"', '\"') + '"' - -def nonblock_read(fname): - """ Read a file with nonblock flag. Return the last line. """ - fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) - buf = os.read(fread, 100000).decode('utf-8', 'replace') - os.close(fread) - - line = buf.rstrip('\r\n') - left = max(line.rfind('\r'), line.rfind('\n')) - if left != -1: - left += 1 - line = line[left:] - - return line - -def main(): - thr.current_thread().name = 'main' - nthreads = int(vim.eval('s:update.threads')) - plugs = vim.eval('s:update.todo') - mac_gui = vim.eval('s:mac_gui') == '1' - - lock = thr.Lock() - buf = Buffer(lock, len(plugs), G_PULL) - buf_q, work_q = queue.Queue(), queue.Queue() - for work in plugs.items(): - work_q.put(work) - - start_cnt = thr.active_count() - for num in range(nthreads): - tname = 'PlugT-{0:02}'.format(num) - thread = PlugThread(tname, (buf_q, work_q, lock)) - thread.start() - if mac_gui: - rthread = RefreshThread(lock) - rthread.start() - - while not buf_q.empty() or thr.active_count() != start_cnt: - try: - action, name, msg = buf_q.get(True, 0.25) - buf.write(action, name, ['OK'] if not msg else msg) - buf_q.task_done() - except queue.Empty: - pass - except KeyboardInterrupt: - G_STOP.set() - - if mac_gui: - rthread.stop() - rthread.join() - -main() -EOF -endfunction - -function! s:update_ruby() - ruby << EOF - module PlugStream - SEP = ["\r", "\n", nil] - def get_line - buffer = '' - loop do - char = readchar rescue return - if SEP.include? char.chr - buffer << $/ - break - else - buffer << char - end - end - buffer - end - end unless defined?(PlugStream) - - def esc arg - %["#{arg.gsub('"', '\"')}"] - end - - def killall pid - pids = [pid] - if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM - pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } - else - unless `which pgrep 2> /dev/null`.empty? - children = pids - until children.empty? - children = children.map { |pid| - `pgrep -P #{pid}`.lines.map { |l| l.chomp } - }.flatten - pids += children - end - end - pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } - end - end - - def compare_git_uri a, b - regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} - regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) - end - - require 'thread' - require 'fileutils' - require 'timeout' - running = true - iswin = VIM::evaluate('s:is_win').to_i == 1 - pull = VIM::evaluate('s:update.pull').to_i == 1 - base = VIM::evaluate('g:plug_home') - all = VIM::evaluate('s:update.todo') - limit = VIM::evaluate('get(g:, "plug_timeout", 60)') - tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 - nthr = VIM::evaluate('s:update.threads').to_i - maxy = VIM::evaluate('winheight(".")').to_i - vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ - cd = iswin ? 'cd /d' : 'cd' - tot = VIM::evaluate('len(s:update.todo)') || 0 - bar = '' - skip = 'Already installed' - mtx = Mutex.new - take1 = proc { mtx.synchronize { running && all.shift } } - logh = proc { - cnt = bar.length - $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" - $curbuf[2] = '[' + bar.ljust(tot) + ']' - VIM::command('normal! 2G') - VIM::command('redraw') - } - where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } - log = proc { |name, result, type| - mtx.synchronize do - ing = ![true, false].include?(type) - bar += type ? '=' : 'x' unless ing - b = case type - when :install then '+' when :update then '*' - when true, nil then '-' else - VIM::command("call add(s:update.errors, '#{name}')") - 'x' - end - result = - if type || type.nil? - ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] - elsif result =~ /^Interrupted|^Timeout/ - ["#{b} #{name}: #{result}"] - else - ["#{b} #{name}"] + result.lines.map { |l| " " << l } - end - if lnum = where.call(name) - $curbuf.delete lnum - lnum = 4 if ing && lnum > maxy - end - result.each_with_index do |line, offset| - $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) - end - logh.call - end - } - bt = proc { |cmd, name, type, cleanup| - tried = timeout = 0 - begin - tried += 1 - timeout += limit - fd = nil - data = '' - if iswin - Timeout::timeout(timeout) do - tmp = VIM::evaluate('tempname()') - system("(#{cmd}) > #{tmp}") - data = File.read(tmp).chomp - File.unlink tmp rescue nil - end - else - fd = IO.popen(cmd).extend(PlugStream) - first_line = true - log_prob = 1.0 / nthr - while line = Timeout::timeout(timeout) { fd.get_line } - data << line - log.call name, line.chomp, type if name && (first_line || rand < log_prob) - first_line = false - end - fd.close - end - [$? == 0, data.chomp] - rescue Timeout::Error, Interrupt => e - if fd && !fd.closed? - killall fd.pid - fd.close - end - cleanup.call if cleanup - if e.is_a?(Timeout::Error) && tried < tries - 3.downto(1) do |countdown| - s = countdown > 1 ? 's' : '' - log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type - sleep 1 - end - log.call name, 'Retrying ...', type - retry - end - [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] - end - } - main = Thread.current - threads = [] - watcher = Thread.new { - if vim7 - while VIM::evaluate('getchar(1)') - sleep 0.1 - end - else - require 'io/console' # >= Ruby 1.9 - nil until IO.console.getch == 3.chr - end - mtx.synchronize do - running = false - threads.each { |t| t.raise Interrupt } unless vim7 - end - threads.each { |t| t.join rescue nil } - main.kill - } - refresh = Thread.new { - while true - mtx.synchronize do - break unless running - VIM::command('noautocmd normal! a') - end - sleep 0.2 - end - } if VIM::evaluate('s:mac_gui') == 1 - - clone_opt = VIM::evaluate('s:clone_opt').join(' ') - progress = VIM::evaluate('s:progress_opt(1)') - nthr.times do - mtx.synchronize do - threads << Thread.new { - while pair = take1.call - name = pair.first - dir, uri, tag = pair.last.values_at *%w[dir uri tag] - exists = File.directory? dir - ok, result = - if exists - chdir = "#{cd} #{iswin ? dir : esc(dir)}" - ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil - current_uri = data.lines.to_a.last - if !ret - if data =~ /^Interrupted|^Timeout/ - [false, data] - else - [false, [data.chomp, "PlugClean required."].join($/)] - end - elsif !compare_git_uri(current_uri, uri) - [false, ["Invalid URI: #{current_uri}", - "Expected: #{uri}", - "PlugClean required."].join($/)] - else - if pull - log.call name, 'Updating ...', :update - fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' - bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil - else - [true, skip] - end - end - else - d = esc dir.sub(%r{[\\/]+$}, '') - log.call name, 'Installing ...', :install - bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { - FileUtils.rm_rf dir - } - end - mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok - log.call name, result, ok - end - } if running - end - end - threads.each { |t| t.join rescue nil } - logh.call - refresh.kill if refresh - watcher.kill -EOF -endfunction - -function! s:shellesc_cmd(arg, script) - let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') - return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') -endfunction - -function! s:shellesc_ps1(arg) - return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" -endfunction - -function! s:shellesc_sh(arg) - return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" -endfunction - -" Escape the shell argument based on the shell. -" Vim and Neovim's shellescape() are insufficient. -" 1. shellslash determines whether to use single/double quotes. -" Double-quote escaping is fragile for cmd.exe. -" 2. It does not work for powershell. -" 3. It does not work for *sh shells if the command is executed -" via cmd.exe (ie. cmd.exe /c sh -c command command_args) -" 4. It does not support batchfile syntax. -" -" Accepts an optional dictionary with the following keys: -" - shell: same as Vim/Neovim 'shell' option. -" If unset, fallback to 'cmd.exe' on Windows or 'sh'. -" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. -function! plug#shellescape(arg, ...) - if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' - return a:arg - endif - let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} - let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') - let script = get(opts, 'script', 1) - if shell =~# 'cmd\(\.exe\)\?$' - return s:shellesc_cmd(a:arg, script) - elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$' - return s:shellesc_ps1(a:arg) - endif - return s:shellesc_sh(a:arg) -endfunction - -function! s:glob_dir(path) - return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') -endfunction - -function! s:progress_bar(line, bar, total) - call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') -endfunction - -function! s:compare_git_uri(a, b) - " See `git help clone' - " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] - " [git@] github.com[:port] : junegunn/vim-plug [.git] - " file:// / junegunn/vim-plug [/] - " / junegunn/vim-plug [/] - let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' - let ma = matchlist(a:a, pat) - let mb = matchlist(a:b, pat) - return ma[1:2] ==# mb[1:2] -endfunction - -function! s:format_message(bullet, name, message) - if a:bullet != 'x' - return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] - else - let lines = map(s:lines(a:message), '" ".v:val') - return extend([printf('x %s:', a:name)], lines) - endif -endfunction - -function! s:with_cd(cmd, dir, ...) - let script = a:0 > 0 ? a:1 : 1 - return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) -endfunction - -function! s:system(cmd, ...) - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(1) - if type(a:cmd) == s:TYPE.list - " Neovim's system() supports list argument to bypass the shell - " but it cannot set the working directory for the command. - " Assume that the command does not rely on the shell. - if has('nvim') && a:0 == 0 - return system(a:cmd) - endif - let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) - if &shell =~# 'powershell\(\.exe\)\?$' - let cmd = '& ' . cmd - endif - else - let cmd = a:cmd - endif - if a:0 > 0 - let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) - endif - if s:is_win && type(a:cmd) != s:TYPE.list - let [batchfile, cmd] = s:batchfile(cmd) - endif - return system(cmd) - finally - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry -endfunction - -function! s:system_chomp(...) - let ret = call('s:system', a:000) - return v:shell_error ? '' : substitute(ret, '\n$', '', '') -endfunction - -function! s:git_validate(spec, check_branch) - let err = '' - if isdirectory(a:spec.dir) - let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] - let remote = result[-1] - if empty(remote) - let err = join([remote, 'PlugClean required.'], "\n") - elseif !s:compare_git_uri(remote, a:spec.uri) - let err = join(['Invalid URI: '.remote, - \ 'Expected: '.a:spec.uri, - \ 'PlugClean required.'], "\n") - elseif a:check_branch && has_key(a:spec, 'commit') - let sha = s:git_revision(a:spec.dir) - if empty(sha) - let err = join(add(result, 'PlugClean required.'), "\n") - elseif !s:hash_match(sha, a:spec.commit) - let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', - \ a:spec.commit[:6], sha[:6]), - \ 'PlugUpdate required.'], "\n") - endif - elseif a:check_branch - let current_branch = result[0] - " Check tag - let origin_branch = s:git_origin_branch(a:spec) - if has_key(a:spec, 'tag') - let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) - if a:spec.tag !=# tag && a:spec.tag !~ '\*' - let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', - \ (empty(tag) ? 'N/A' : tag), a:spec.tag) - endif - " Check branch - elseif origin_branch !=# current_branch - let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', - \ current_branch, origin_branch) - endif - if empty(err) - let [ahead, behind] = split(s:lastline(s:system([ - \ 'git', 'rev-list', '--count', '--left-right', - \ printf('HEAD...origin/%s', origin_branch) - \ ], a:spec.dir)), '\t') - if !v:shell_error && ahead - if behind - " Only mention PlugClean if diverged, otherwise it's likely to be - " pushable (and probably not that messed up). - let err = printf( - \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" - \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) - else - let err = printf("Ahead of origin/%s by %d commit(s).\n" - \ .'Cannot update until local changes are pushed.', - \ origin_branch, ahead) - endif - endif - endif - endif - else - let err = 'Not found' - endif - return [err, err =~# 'PlugClean'] -endfunction - -function! s:rm_rf(dir) - if isdirectory(a:dir) - return s:system(s:is_win - \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) - \ : ['rm', '-rf', a:dir]) - endif -endfunction - -function! s:clean(force) - call s:prepare() - call append(0, 'Searching for invalid plugins in '.g:plug_home) - call append(1, '') - - " List of valid directories - let dirs = [] - let errs = {} - let [cnt, total] = [0, len(g:plugs)] - for [name, spec] in items(g:plugs) - if !s:is_managed(name) - call add(dirs, spec.dir) - else - let [err, clean] = s:git_validate(spec, 1) - if clean - let errs[spec.dir] = s:lines(err)[0] - else - call add(dirs, spec.dir) - endif - endif - let cnt += 1 - call s:progress_bar(2, repeat('=', cnt), total) - normal! 2G - redraw - endfor - - let allowed = {} - for dir in dirs - let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 - let allowed[dir] = 1 - for child in s:glob_dir(dir) - let allowed[child] = 1 - endfor - endfor - - let todo = [] - let found = sort(s:glob_dir(g:plug_home)) - while !empty(found) - let f = remove(found, 0) - if !has_key(allowed, f) && isdirectory(f) - call add(todo, f) - call append(line('$'), '- ' . f) - if has_key(errs, f) - call append(line('$'), ' ' . errs[f]) - endif - let found = filter(found, 'stridx(v:val, f) != 0') - end - endwhile - - 4 - redraw - if empty(todo) - call append(line('$'), 'Already clean.') - else - let s:clean_count = 0 - call append(3, ['Directories to delete:', '']) - redraw! - if a:force || s:ask_no_interrupt('Delete all directories?') - call s:delete([6, line('$')], 1) - else - call setline(4, 'Cancelled.') - nnoremap 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 - let err_count = 0 - while l1 <= l2 - let line = getline(l1) - if line =~ '^- ' && isdirectory(line[2:]) - execute l1 - redraw! - let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) - let force = force || answer > 1 - if answer - let err = s:rm_rf(line[2:]) - setlocal modifiable - if empty(err) - call setline(l1, '~'.line[1:]) - let s:clean_count += 1 - else - delete _ - call append(l1 - 1, s:format_message('x', line[1:], err)) - let l2 += len(s:lines(err)) - let err_count += 1 - endif - let msg = printf('Removed %d directories.', s:clean_count) - if err_count > 0 - let msg .= printf(' Failed to remove %d directories.', err_count) - endif - call setline(4, msg) - setlocal nomodifiable - endif - endif - let l1 += 1 - endwhile -endfunction - -function! s:upgrade() - echo 'Downloading the latest version of vim-plug' - redraw - let tmp = s:plug_tempname() - let new = tmp . '/plug.vim' - - try - let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) - if v:shell_error - return s:err('Error upgrading vim-plug: '. out) - endif - - if readfile(s:me) ==# readfile(new) - echo 'vim-plug is already up-to-date' - return 0 - else - call rename(s:me, s:me . '.old') - call rename(new, s:me) - unlet g:loaded_plug - echo 'vim-plug has been upgraded' - return 1 - endif - finally - silent! call s:rm_rf(tmp) - endtry -endfunction - -function! s:upgrade_specs() - for spec in values(g:plugs) - let spec.frozen = get(spec, 'frozen', 0) - endfor -endfunction - -function! s:status() - call s:prepare() - call append(0, 'Checking plugins') - call append(1, '') - - let ecnt = 0 - let unloaded = 0 - let [cnt, total] = [0, len(g:plugs)] - for [name, spec] in items(g:plugs) - let is_dir = isdirectory(spec.dir) - if has_key(spec, 'uri') - if is_dir - let [err, _] = s:git_validate(spec, 1) - let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] - else - let [valid, msg] = [0, 'Not found. Try PlugInstall.'] - endif - else - if is_dir - let [valid, msg] = [1, 'OK'] - else - let [valid, msg] = [0, 'Not found.'] - endif - endif - let cnt += 1 - let ecnt += !valid - " `s:loaded` entry can be missing if PlugUpgraded - if is_dir && get(s:loaded, name, -1) == 0 - let unloaded = 1 - let msg .= ' (not loaded)' - endif - call s:progress_bar(2, repeat('=', cnt), total) - call append(3, s:format_message(valid ? '-' : 'x', name, msg)) - normal! 2G - redraw - endfor - call setline(1, 'Finished. '.ecnt.' error(s).') - normal! gg - setlocal nomodifiable - if unloaded - echo "Press 'L' on each line to load plugin, or 'U' to update" - nnoremap 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 - let batchfile = '' - try - let [sh, shellcmdflag, shrd] = s:chsh(1) - let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha - if s:is_win - let [batchfile, cmd] = s:batchfile(cmd) - endif - execute 'silent %!' cmd - finally - let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] - if s:is_win && filereadable(batchfile) - call delete(batchfile) - endif - endtry - setlocal nomodifiable - nnoremap 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 branch = s:git_origin_branch(v) - if len(branch) - let range = origin ? '..origin/'.branch : 'HEAD@{1}..' - let cmd = ['git', 'log', '--graph', '--color=never'] - if s:git_version_requirement(2, 10, 0) - call add(cmd, '--no-show-signature') - endif - call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) - if has_key(v, 'rtp') - call extend(cmd, ['--', v.rtp]) - endif - let diff = s:system_chomp(cmd, v.dir) - if !empty(diff) - let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' - call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) - let cnts[origin] += 1 - endif - endif - let bar .= '=' - call s:progress_bar(2, bar, len(total)) - normal! 2G - redraw - endfor - if !cnts[origin] - call append(5, ['', 'N/A']) - endif - endfor - call setline(1, printf('%d plugin(s) updated.', cnts[0]) - \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) - - if cnts[0] || cnts[1] - nnoremap (plug-preview) :silent! call preview_commit() - if empty(maparg("\", 'n')) - nmap (plug-preview) - endif - if empty(maparg('o', 'n')) - nmap o (plug-preview) - endif - endif - if cnts[0] - nnoremap X :call revert() - echo "Press 'X' on each block to revert the update" - endif - normal! gg - setlocal nomodifiable -endfunction - -function! s:revert() - if search('^Pending updates', 'bnW') - return - endif - - let name = s:find_name(line('.')) - if empty(name) || !has_key(g:plugs, name) || - \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' - return - endif - - call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) - setlocal modifiable - normal! "_dap - setlocal nomodifiable - echo 'Reverted' -endfunction - -function! s:snapshot(force, ...) abort - call s:prepare() - setf vim - call append(0, ['" Generated by vim-plug', - \ '" '.strftime("%c"), - \ '" :source this file in vim to restore the snapshot', - \ '" or execute: vim -S snapshot.vim', - \ '', '', 'PlugUpdate!']) - 1 - let anchor = line('$') - 3 - let names = sort(keys(filter(copy(g:plugs), - \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) - for name in reverse(names) - let sha = s:git_revision(g:plugs[name].dir) - if !empty(sha) - call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) - redraw - endif - endfor - - if a:0 > 0 - let fn = s:plug_expand(a:1) - if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) - return - endif - call writefile(getline(1, '$'), fn) - echo 'Saved as '.a:1 - silent execute 'e' s:esc(fn) - setf vim - endif -endfunction - -function! s:split_rtp() - return split(&rtp, '\\\@ Date: Sun, 31 Jul 2022 06:46:27 -0700 Subject: [PATCH 15/25] again --- nvim/.vimrc | 244 ------------------------------------------------- nvim/README.md | 0 nvim/init.vim | 5 - nvim/nvim | 1 - 4 files changed, 250 deletions(-) delete mode 100644 nvim/.vimrc delete mode 100644 nvim/README.md delete mode 100644 nvim/init.vim delete mode 160000 nvim/nvim diff --git a/nvim/.vimrc b/nvim/.vimrc deleted file mode 100644 index 93aa08d..0000000 --- a/nvim/.vimrc +++ /dev/null @@ -1,244 +0,0 @@ -" Basic -" set shell=/bin/zsh -set belloff=all " Annoying bell off -set encoding=utf-8 -set nocompatible " Set no compatible with vi -set noswapfile - -set hidden -set ruler -set laststatus=2 -set number -set nocursorline -set lazyredraw -set title "Show the filename in the window title bar -set nostartofline "Make j/k respect the columns - -set autoindent -set autoread -set backspace=indent,eol,start -set display+=lastline -set foldmethod=syntax -set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ -set nofoldenable -set completeopt-=preview "Disable the preview window for completions. -set expandtab -set smarttab -set splitbelow splitright "Pane splitting -set noshowmode "The status line will show the mode. -set nowrap -set nrformats-=octal -set scrolloff=1 -set sidescrolloff=5 - -set re=1 "Force the old regex engine on any version newer -set wildmenu -set wildmode=longest:full " displayes longest match first - -set shiftwidth=2 -set softtabstop=2 -set tabstop=2 - -set incsearch -set ignorecase -set smartcase - -let &t_SI.="\e[5 q" "SI = INSERT mode -let &t_EI.="\e[1 q" - -"Read/Write mappings -inoremap :w -nnoremap :w -inoremap :wq! -nnoremap :wq! -inoremap :qa! -nnoremap :qa! - -" Pressing Ctrl-u deletes text you've typed in the current line -" Ctrl-w deletes the word before the cursor, both undoable. -" This adds Ctrl-g first to start a new change -inoremap u -inoremap u -inoremap u - -" Clipboard Settings -noremap y "*y -noremap p "*p - -" Uunset highlighting on matches -nnoremap :nohlsearch - -" Exit terminal mode -tnoremap - -" Map buffer list -nnoremap b :ls:b - -" fzf fuzzy search -nmap t :Files -nmap r :Tags - -" Autoreload external changes -au FocusGained,BufEnter * :checktime - -" Session storage directories and viminfo -set viminfo='100,f1,<50,s10,h,n~/.config/nvim/viminfo -set backupdir=~/.config/nvim/.backup// -set directory=~/.config/nvim/.swap// - -" Find snake_case - '+', '_' to convert -:nnoremap + /\w\+_ -:nnoremap _ f_x~ - -"Execute %!python -m json.tool to format json -runtime! macros/matchit.vim - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Plugins -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -call plug#begin('~/.vim/plugged') - "Basics - Plug 'roxma/vim-hug-neovim-rpc' - Plug 'tpope/vim-eunuch' - Plug 'junegunn/vim-easy-align' -" (Optional) Multi-entry selection UI. - Plug 'junegunn/fzf' - Plug 'Chiel92/vim-autoformat' " vim autoformat - Plug 'w0rp/ale' - Plug 'tell-k/vim-autopep8' - Plug 'jiangmiao/auto-pairs' - Plug 'tpope/vim-surround' " selection surroundings - Plug 'scrooloose/nerdcommenter' " code commenting - "Visuals - Plug 'flazz/vim-colorschemes' - Plug 'ap/vim-css-color' " CSS Vim color preview - "Language-Specific (alphabetical by package-identifier - Plug 'chr4/nginx.vim' " nginx configs - Plug 'mxw/vim-jsx' " JSX highlighter (depends on underlying JS highlighter - Plug 'HerringtonDarkholme/yats.vim' - Plug 'mhartington/nvim-typescript', {'do': ':!install.sh \| UpdateRemotePlugins'} - - "FZF Fuzzy Search - Plug 'junegunn/fzf', { 'build': './install --all', 'merged': 0 } - Plug 'junegunn/fzf.vim', { 'depends': 'fzf' } - -call plug#end() - -nnoremap :call LanguageClient_contextMenu() -" Or map each action separately -" nnoremap K :call LanguageClient#textDocument_hover() -" nnoremap gd :call LanguageClient#textDocument_definition() -" nnoremap :call LanguageClient#textDocument_rename() - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Color Scheme -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -filetype plugin indent on -syntax enable -" This line enables the true color support. -let $NVIM_TUI_ENABLE_TRUE_COLOR=1 -set termguicolors -set background=dark - -let g:jellybeans_use_lowcolor_black = 1 -let g:jellybeans_overrides = { -\ 'Todo': { 'guifg': '303030', 'guibg': 'f0f000', -\ 'ctermfg': 'Black', 'ctermbg': 'Yellow', -\ 'attr': 'bold' }, -\ 'Comment': { 'guifg': 'cccccc' }, -\} - -let g:jellybeans_overrides = { -\ 'MatchParen': { 'guifg': 'dd0093', 'guibg': '000000', -\ 'ctermfg': 'Magenta', 'ctermbg': '' }, -\} -colorscheme jellybeans - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Autopair Completion -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -let g:AutoPairsFlyMode = 0 -let g:AutoPairsShortcutBackInsert = '' -let g:AutoPairsShortcutJump = 0 - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Lightline -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -let g:lightline = { - \ 'colorscheme': 'powerline', - \ 'active': { - \ 'left': [ [ 'mode', 'paste' ], - \ [ 'readonly', 'absolutepath', 'modified' ] ], - \ 'right': [ [ 'custom-lineinfo' ], - \ [ 'custom-fileinfo' ] ], - \ }, - \ 'inactive': { - \ 'left': [ [ 'readonly', 'filename', 'modified' ] ], - \ 'right': [ [ 'custom-lineinfo' ], - \ [ 'custom-fileinfo' ] ], - \ }, - \ 'component': { - \ 'custom-lineinfo': ' %3p%% ┃ %4l/%L :%3c', - \ }, - \ 'component_function': { - \ 'custom-fileinfo': 'LightlineFileInfo', - \ 'readonly': 'LightlineReadonly', - \ }, - \ 'tabline': { - \ 'left': [ [ 'tabs' ] ], - \ 'right': [ ], - \ }, -\ } - -function! LightlineReadonly() - return &readonly ? '∄' : '' -endfunction -function! LightlineFileInfo() - let displayFiletype = &filetype !=# '' ? &filetype : 'no ft' - return ' ' . displayFiletype . ' ┃ ' . &fileencoding . '[' . &fileformat . '] ' -endfunction - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Completions -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Completions -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -"Autocomplete and cycle from top-to-bottom of suggestions using . -inoremap pumvisible() ? "\" : "\" - -": completion back. -inoremap pumvisible() ? "\" : "\" -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Python Autopep 8 Formatting -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -let g:autopep8_max_line_length=79 -let g:autopep8_ignore="E501,W293,C0321" -let g:autopep8_disable_show_diff=0 -let g:autopep8_on_save=1 " Autosave on save - -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Automatic Whitespace Trimming and Formatting (for select filetypes) -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -let g:ale_typescript_tslint_ignore_empty_files = 0 -let g:ale_typescript_tsserver_config_path = '' -let g:ale_typescript_tsserver_executable = 'tsserver' -let g:ale_typescript_tsserver_use_global = 0 - -let g:jsx_ext_required = 0 "jsx highlighting - -""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -" Ale Syntax & Language Specific -"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" -let g:ale_sign_error = '✘' -let g:ale_sign_warning = '⚠' -let g:ale_fix_on_save = 1 -let g:ale_fixers = { -\ '*': ['remove_trailing_lines', 'trim_whitespace'], -\ 'javascript': ['prettier', 'eslint'], -\ 'typescript': ['prettier', 'tslint'], -\} -highlight ALEErrorSign ctermbg=NONE ctermfg=red -highlight ALEWarningSign ctermbg=NONE ctermfg=yellow diff --git a/nvim/README.md b/nvim/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/nvim/init.vim b/nvim/init.vim deleted file mode 100644 index 55a1858..0000000 --- a/nvim/init.vim +++ /dev/null @@ -1,5 +0,0 @@ -source $HOME/.config/nvim/config/init.vimrc -source $HOME/.config/nvim/config/general.vimrc -source $HOME/.config/nvim/config/plugins.vimrc -source $HOME/.config/nvim/config/keys.vimrc -source $HOME/.config/nvim/config/line.vimrc diff --git a/nvim/nvim b/nvim/nvim deleted file mode 160000 index 7ee31c0..0000000 --- a/nvim/nvim +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7ee31c09e13abc0956d341a12357bfa70323d41d From 2f913fc4fb350614463e24a2e72c82beff7142f5 Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Sun, 31 Jul 2022 06:47:59 -0700 Subject: [PATCH 16/25] update --- nvim/.gitignore | 5 + nvim/README.md | 54 + nvim/assets/images/nvim-readme.png | Bin 0 -> 184631 bytes nvim/autoload/.gitignore | 1 + nvim/autoload/plug.vim | 2801 ++++++++++++++++++++++++++++ nvim/autoload/plug.vim.old | 2797 +++++++++++++++++++++++++++ nvim/config/.gitignore | 1 + nvim/config/general.vimrc | 138 ++ nvim/config/init.vimrc | 81 + nvim/config/keys.vimrc | 136 ++ nvim/config/line.vimrc | 53 + nvim/config/plugins.vimrc | 127 ++ nvim/init.vim | 5 + nvim/scripts/install-in-ubuntu.sh | 18 + 14 files changed, 6217 insertions(+) create mode 100644 nvim/.gitignore create mode 100644 nvim/README.md create mode 100644 nvim/assets/images/nvim-readme.png create mode 100644 nvim/autoload/.gitignore create mode 100644 nvim/autoload/plug.vim create mode 100644 nvim/autoload/plug.vim.old create mode 100644 nvim/config/.gitignore create mode 100644 nvim/config/general.vimrc create mode 100644 nvim/config/init.vimrc create mode 100644 nvim/config/keys.vimrc create mode 100644 nvim/config/line.vimrc create mode 100644 nvim/config/plugins.vimrc create mode 100644 nvim/init.vim create mode 100644 nvim/scripts/install-in-ubuntu.sh diff --git a/nvim/.gitignore b/nvim/.gitignore new file mode 100644 index 0000000..5394625 --- /dev/null +++ b/nvim/.gitignore @@ -0,0 +1,5 @@ +backup/ +plugged/ +undodir/ +fonts/ +.netrwhist diff --git a/nvim/README.md b/nvim/README.md new file mode 100644 index 0000000..59751d5 --- /dev/null +++ b/nvim/README.md @@ -0,0 +1,54 @@ +# nvim +A neovim configuration repo with many plugins that for more easily to install and configure your neovim! + +## QUICK way to get it! +> For ubuntu + +#### 1. First of all install them + +```shell +bash <(curl -s https://raw.githubusercontent.com/hiyali/nvim/master/scripts/install-in-ubuntu.sh) +``` + +#### 2. And then install plugins! + +```shell +nvim ~/.config/nvim/config/init.vimrc +:PlugInstall +``` + +--- + +## Requirements +* [neovim](https://github.com/neovim/neovim/wiki/Installing-Neovim) +* [python3](https://www.python.org/) +* [nerd-fonts](https://github.com/ryanoasis/nerd-fonts#font-installation) + +## Install & Configure +### 1. Install - Homebrew (macOS) / Linuxbrew (Linux) +```shell +brew install neovim +pip3 install neovim +``` + +### 2. Font +```shell +cd ~/Library/Fonts && curl -fLo "Droid Sans Mono for Powerline Nerd Font Complete.otf" https://raw.githubusercontent.com/ryanoasis/nerd-fonts/master/patched-fonts/DroidSansMono/complete/Droid%20Sans%20Mono%20for%20Powerline%20Nerd%20Font%20Complete.otf +``` +And set your terminal fonts to "Droid Sans Mono for Powerline Nerd Font Complete" + +### 3. Configurations & Plugins +```shell +git clone https://github.com/hiyali/nvim.git ~/.config/nvim +nvim ~/.config/nvim/config/init.vimrc +:PlugInstall +``` + +## Usage docs +[wiki](https://github.com/hiyali/nvim/wiki) + +## Screenshot +![nvim](https://raw.githubusercontent.com/hiyali/nvim/master/assets/images/nvim-readme.png "nvim") + +## Contribute +> Feel free diff --git a/nvim/assets/images/nvim-readme.png b/nvim/assets/images/nvim-readme.png new file mode 100644 index 0000000000000000000000000000000000000000..34b0d26d51b38e0e9a3577d62879cde8c51fdd2e GIT binary patch literal 184631 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

d9c@qjdL!&Q|LX;$#Ey)Lk?eW6uC zN`Pn+oO6||srjLd89V�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 G>3;y_^1>_t literal 0 HcmV?d00001 diff --git a/nvim/autoload/.gitignore b/nvim/autoload/.gitignore new file mode 100644 index 0000000..e774117 --- /dev/null +++ b/nvim/autoload/.gitignore @@ -0,0 +1 @@ +!plug.vim diff --git a/nvim/autoload/plug.vim b/nvim/autoload/plug.vim new file mode 100644 index 0000000..6a958cb --- /dev/null +++ b/nvim/autoload/plug.vim @@ -0,0 +1,2801 @@ +" vim-plug: Vim plugin manager +" ============================ +" +" Download plug.vim and put it in ~/.vim/autoload +" +" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" +" Edit your .vimrc +" +" call plug#begin('~/.vim/plugged') +" +" " Make sure you use single quotes +" +" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align +" Plug 'junegunn/vim-easy-align' +" +" " Any valid git URL is allowed +" Plug 'https://github.com/junegunn/vim-github-dashboard.git' +" +" " Multiple Plug commands can be written in a single line using | separators +" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' +" +" " On-demand loading +" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } +" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } +" +" " Using a non-default branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } +" +" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) +" Plug 'fatih/vim-go', { 'tag': '*' } +" +" " Plugin options +" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } +" +" " Plugin outside ~/.vim/plugged with post-update hook +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +" +" " Unmanaged plugin (manually installed and updated) +" Plug '~/my-prototype-plugin' +" +" " Initialize plugin system +" call plug#end() +" +" Then reload .vimrc and :PlugInstall to install plugins. +" +" Plug options: +" +"| Option | Description | +"| ----------------------- | ------------------------------------------------ | +"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | +"| `rtp` | Subdirectory that contains Vim plugin | +"| `dir` | Custom directory for the plugin | +"| `as` | Use different name for the plugin | +"| `do` | Post-update hook (string or funcref) | +"| `on` | On-demand loading: Commands or ``-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand(':p')) + set shellslash +else + let s:me = resolve(expand(':p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! s:is_powershell(shell) + return a:shell =~# 'powershell\(\.exe\)\?$' || a:shell =~# 'pwsh\(\.exe\)\?$' +endfunction + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + if has('win32') + \ && &shellslash + \ && (&shell =~# 'cmd\(\.exe\)\?$' || s:is_powershell(&shell)) + return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') + endif + if !has('nvim') + \ && (has('win32') || has('win32unix')) + \ && !has('multi_byte') + return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) + command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) + command! -nargs=0 -bar -bang PlugClean call s:clean(0) + 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! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('plug#end() called without calling plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') + let s:loaded[name] = 1 + continue + endif + + if has_key(plug, 'on') + let s:triggers[name] = { 'map': [], 'cmd': [] } + for cmd in s:to_a(plug.on) + if cmd =~? '^.\+' + 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 + +function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) +endfunction + +if s:is_win + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction + + " Copied from fzf + function! s:wrap_cmds(cmds) + let cmds = [ + \ '@echo off', + \ 'setlocal enabledelayedexpansion'] + \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) + \ + ['endlocal'] + if has('iconv') + if !exists('s:codepage') + let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) + endif + return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) + endif + return map(cmds, 'v:val."\r"') + endfunction + + function! s:batchfile(cmd) + let batchfile = s:plug_tempname().'.bat' + call writefile(s:wrap_cmds(a:cmd), batchfile) + let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) + endif +endfunction + +function! s:dobufread(names) + for name in a:names + let path = s:rtp(g:plugs[name]) + for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] + if len(finddir(dir, path)) + if exists('#BufRead') + doautocmd BufRead + endif + return + endif + endfor + endfor +endfunction + +function! plug#load(...) + if a:0 == 0 + return s:err('Argument missing: plugin name(s) required') + endif + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 + let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') + if !empty(unknowns) + let s = len(unknowns) > 1 ? 's' : '' + return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) + end + let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') + if !empty(unloaded) + for name in unloaded + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + call s:dobufread(unloaded) + return 1 + end + return 0 +endfunction + +function! s:remove_triggers(name) + if !has_key(s:triggers, a:name) + return + endif + for cmd in s:triggers[a:name].cmd + execute 'silent! delc' cmd + endfor + for map in s:triggers[a:name].map + execute 'silent! unmap' map + execute 'silent! iunmap' map + endfor + call remove(s:triggers, a:name) +endfunction + +function! s:lod(names, types, ...) + for name in a:names + call s:remove_triggers(name) + let s:loaded[name] = 1 + endfor + call s:reorg_rtp() + + for name in a:names + let rtp = s:rtp(g:plugs[name]) + for dir in a:types + call s:source(rtp, dir.'/**/*.vim') + endfor + if a:0 + if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) + execute 'runtime' a:1 + endif + call s:source(rtp, a:2) + endif + call s:doautocmd('User', name) + endfor +endfunction + +function! s:lod_ft(pat, names) + let syn = 'syntax/'.a:pat.'.vim' + call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) + execute 'autocmd! PlugLOD FileType' a:pat + call s:doautocmd('filetypeplugin', 'FileType') + call s:doautocmd('filetypeindent', 'FileType') +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) +endfunction + +function! s:lod_map(map, names, with_prefix, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + + if a:with_prefix + let prefix = v:count ? v:count : '' + let prefix .= '"'.v:register.a:prefix + if mode(1) == 'no' + if v:operator == 'c' + let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(repo . ' ' . v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' + if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif + let opts.tag = a:arg + elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-\{1}\ / + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap 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(s:plug_getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap q :call close_pane() + 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 + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + else + bd + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win + set shell=sh + endif + if a:swap + if s:is_powershell(&shell) + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' + set shellredir=>%s\ 2>&1 + endif + endif + return prev +endfunction + +function! s:bang(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') + execute "normal! :execute g:_plug_bang\\" + finally + unlet g:_plug_bang + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + call s:load_plugin(spec) + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:git_revision(a:spec.dir) + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' + let output = s:system( + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = [] + if get(g:, 'plug_shallow', 1) + call extend(s:clone_opt, ['--depth', '1']) + if s:git_version_requirement(1, 7, 10) + call add(s:clone_opt, '--no-single-branch') + endif + endif + + if has('win32unix') || has('wsl') + call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = s:git_origin_branch(spec) + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return (a:event == 'stdout' || a:event == 'stderr') ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + + if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = a:cmd + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = s:plug_call('jobstart', argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) + if has_key(a:opts, 'dir') + let cmd = s:with_cd(cmd, a:opts.dir, 0) + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'err_mode': 'raw', + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + let max = line('$') + for i in range(4, max > 4 ? max : 4) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, max > 5 ? max : 5) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = empty(globpath(spec.dir, '.git', 1)) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, cmd, { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + let cmd = ['git', 'clone'] + if !has_tag + call extend(cmd, s:clone_opt) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt').join(' ') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') +endfunction + +function! s:shellesc_ps1(arg) + return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" +endfunction + +function! s:shellesc_sh(arg) + return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" +endfunction + +" Escape the shell argument based on the shell. +" Vim and Neovim's shellescape() are insufficient. +" 1. shellslash determines whether to use single/double quotes. +" Double-quote escaping is fragile for cmd.exe. +" 2. It does not work for powershell. +" 3. It does not work for *sh shells if the command is executed +" via cmd.exe (ie. cmd.exe /c sh -c command command_args) +" 4. It does not support batchfile syntax. +" +" Accepts an optional dictionary with the following keys: +" - shell: same as Vim/Neovim 'shell' option. +" If unset, fallback to 'cmd.exe' on Windows or 'sh'. +" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. +function! plug#shellescape(arg, ...) + if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' + return a:arg + endif + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\(\.exe\)\?$' + return s:shellesc_cmd(a:arg, script) + elseif s:is_powershell(shell) + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) +endfunction + +function! s:system(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + if type(a:cmd) == s:TYPE.list + " Neovim's system() supports list argument to bypass the shell + " but it cannot set the working directory for the command. + " Assume that the command does not rely on the shell. + if has('nvim') && a:0 == 0 + return system(a:cmd) + endif + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) + if s:is_powershell(&shell) + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd + endif + if a:0 > 0 + let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) + endif + if s:is_win && type(a:cmd) != s:TYPE.list + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] + let remote = result[-1] + if empty(remote) + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let sha = s:git_revision(a:spec.dir) + if empty(sha) + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let current_branch = result[0] + " Check tag + let origin_branch = s:git_origin_branch(a:spec) + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif origin_branch !=# current_branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ current_branch, origin_branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ origin_branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap 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 + let err_count = 0 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + let err = s:rm_rf(line[2:]) + setlocal modifiable + if empty(err) + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + else + delete _ + call append(l1 - 1, s:format_message('x', line[1:], err)) + let l2 += len(s:lines(err)) + let err_count += 1 + endif + let msg = printf('Removed %d directories.', s:clean_count) + if err_count > 0 + let msg .= printf(' Failed to remove %d directories.', err_count) + endif + call setline(4, msg) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) + if v:shell_error + return s:err('Error upgrading vim-plug: '. out) + endif + + if readfile(s:me) ==# readfile(new) + echo 'vim-plug is already up-to-date' + return 0 + else + call rename(s:me, s:me . '.old') + call rename(new, s:me) + unlet g:loaded_plug + echo 'vim-plug has been upgraded' + return 1 + endif + finally + silent! call s:rm_rf(tmp) + endtry +endfunction + +function! s:upgrade_specs() + for spec in values(g:plugs) + let spec.frozen = get(spec, 'frozen', 0) + endfor +endfunction + +function! s:status() + call s:prepare() + call append(0, 'Checking plugins') + call append(1, '') + + let ecnt = 0 + let unloaded = 0 + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + let is_dir = isdirectory(spec.dir) + if has_key(spec, 'uri') + if is_dir + let [err, _] = s:git_validate(spec, 1) + let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if is_dir + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif + endif + let cnt += 1 + let ecnt += !valid + " `s:loaded` entry can be missing if PlugUpgraded + if is_dir && get(s:loaded, name, -1) == 0 + let unloaded = 1 + let msg .= ' (not loaded)' + endif + call s:progress_bar(2, repeat('=', cnt), total) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) + normal! 2G + redraw + endfor + call setline(1, 'Finished. '.ecnt.' error(s).') + normal! gg + setlocal nomodifiable + if unloaded + echo "Press 'L' on each line to load plugin, or 'U' to update" + nnoremap 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 + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap 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 branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap (plug-preview) :silent! call preview_commit() + if empty(maparg("\", 'n')) + nmap (plug-preview) + endif + if empty(maparg('o', 'n')) + nmap o (plug-preview) + endif + endif + if cnts[0] + nnoremap X :call revert() + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = s:git_revision(g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@`-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) +let s:vim8 = has('patch-8.0.0039') && exists('*job_start') +if s:is_win && &shellslash + set noshellslash + let s:me = resolve(expand(':p')) + set shellslash +else + let s:me = resolve(expand(':p')) +endif +let s:base_spec = { 'branch': '', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +function! s:isabsolute(dir) abort + return a:dir =~# '^/' || (has('win32') && a:dir =~? '^\%(\\\|[A-Z]:\)') +endfunction + +function! s:git_dir(dir) abort + let gitdir = s:trim(a:dir) . '/.git' + if isdirectory(gitdir) + return gitdir + endif + if !filereadable(gitdir) + return '' + endif + let gitdir = matchstr(get(readfile(gitdir), 0, ''), '^gitdir: \zs.*') + if len(gitdir) && !s:isabsolute(gitdir) + let gitdir = a:dir . '/' . gitdir + endif + return isdirectory(gitdir) ? gitdir : '' +endfunction + +function! s:git_origin_url(dir) abort + let gitdir = s:git_dir(a:dir) + let config = gitdir . '/config' + if empty(gitdir) || !filereadable(config) + return '' + endif + return matchstr(join(readfile(config)), '\[remote "origin"\].\{-}url\s*=\s*\zs\S*\ze') +endfunction + +function! s:git_revision(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + + let line = get(readfile(head), 0, '') + let ref = matchstr(line, '^ref: \zs.*') + if empty(ref) + return line + endif + + if filereadable(gitdir . '/' . ref) + return get(readfile(gitdir . '/' . ref), 0, '') + endif + + if filereadable(gitdir . '/packed-refs') + for line in readfile(gitdir . '/packed-refs') + if line =~# ' ' . ref + return matchstr(line, '^[0-9a-f]*') + endif + endfor + endif + + return '' +endfunction + +function! s:git_local_branch(dir) abort + let gitdir = s:git_dir(a:dir) + let head = gitdir . '/HEAD' + if empty(gitdir) || !filereadable(head) + return '' + endif + let branch = matchstr(get(readfile(head), 0, ''), '^ref: refs/heads/\zs.*') + return len(branch) ? branch : 'HEAD' +endfunction + +function! s:git_origin_branch(spec) + if len(a:spec.branch) + return a:spec.branch + endif + + " The file may not be present if this is a local repository + let gitdir = s:git_dir(a:spec.dir) + let origin_head = gitdir.'/refs/remotes/origin/HEAD' + if len(gitdir) && filereadable(origin_head) + return matchstr(get(readfile(origin_head), 0, ''), + \ '^ref: refs/remotes/origin/\zs.*') + endif + + " The command may not return the name of a branch in detached HEAD state + let result = s:lines(s:system('git symbolic-ref --short HEAD', a:spec.dir)) + return v:shell_error ? '' : result[-1] +endfunction + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + if !executable('git') + return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') + endif + if has('win32') + \ && &shellslash + \ && (&shell =~# 'cmd\(\.exe\)\?$' || &shell =~# 'powershell\(\.exe\)\?$') + return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') + endif + if !has('nvim') + \ && (has('win32') || has('win32unix')) + \ && !has('multi_byte') + return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') + endif + command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(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! s:lazy(plug, opt) + return has_key(a:plug, a:opt) && + \ (empty(s:to_a(a:plug[a:opt])) || + \ !isdirectory(a:plug.dir) || + \ len(s:glob(s:rtp(a:plug), 'plugin')) || + \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) +endfunction + +function! plug#end() + if !exists('g:plugs') + return s:err('plug#end() called without calling plug#begin() first') + endif + + if exists('#PlugLOD') + augroup PlugLOD + autocmd! + augroup END + augroup! PlugLOD + endif + let lod = { 'ft': {}, 'map': {}, 'cmd': {} } + + if exists('g:did_load_filetypes') + filetype off + endif + for name in g:plugs_order + if !has_key(g:plugs, name) + continue + endif + let plug = g:plugs[name] + if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') + let s:loaded[name] = 1 + continue + endif + + if has_key(plug, 'on') + let s:triggers[name] = { 'map': [], 'cmd': [] } + for cmd in s:to_a(plug.on) + if cmd =~? '^.\+' + 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 + +function! s:rtp(spec) + return s:path(a:spec.dir . get(a:spec, 'rtp', '')) +endfunction + +if s:is_win + function! s:path(path) + return s:trim(substitute(a:path, '/', '\', 'g')) + endfunction + + function! s:dirpath(path) + return s:path(a:path) . '\' + endfunction + + function! s:is_local_plug(repo) + return a:repo =~? '^[a-z]:\|^[%~]' + endfunction + + " Copied from fzf + function! s:wrap_cmds(cmds) + let cmds = [ + \ '@echo off', + \ 'setlocal enabledelayedexpansion'] + \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) + \ + ['endlocal'] + if has('iconv') + if !exists('s:codepage') + let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) + endif + return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) + endif + return map(cmds, 'v:val."\r"') + endfunction + + function! s:batchfile(cmd) + let batchfile = s:plug_tempname().'.bat' + call writefile(s:wrap_cmds(a:cmd), batchfile) + let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) + if &shell =~# 'powershell\(\.exe\)\?$' + let cmd = '& ' . cmd + endif + return [batchfile, cmd] + endfunction +else + function! s:path(path) + return s:trim(a:path) + endfunction + + function! s:dirpath(path) + return substitute(a:path, '[/\\]*$', '/', '') + endfunction + + function! s:is_local_plug(repo) + return a:repo[0] =~ '[/$~]' + endfunction +endif + +function! s:err(msg) + echohl ErrorMsg + echom '[vim-plug] '.a:msg + echohl None +endfunction + +function! s:warn(cmd, msg) + echohl WarningMsg + execute a:cmd 'a:msg' + echohl None +endfunction + +function! s:esc(path) + return escape(a:path, ' ') +endfunction + +function! s:escrtp(path) + return escape(a:path, ' ,') +endfunction + +function! s:remove_rtp() + for name in s:loaded_names() + let rtp = s:rtp(g:plugs[name]) + execute 'set rtp-='.s:escrtp(rtp) + let after = globpath(rtp, 'after') + if isdirectory(after) + execute 'set rtp-='.s:escrtp(after) + endif + endfor +endfunction + +function! s:reorg_rtp() + if !empty(s:first_rtp) + execute 'set rtp-='.s:first_rtp + execute 'set rtp-='.s:last_rtp + endif + + " &rtp is modified from outside + if exists('s:prtp') && s:prtp !=# &rtp + call s:remove_rtp() + unlet! s:middle + endif + + let s:middle = get(s:, 'middle', &rtp) + let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') + let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') + let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') + \ . ','.s:middle.',' + \ . join(map(afters, 'escape(v:val, ",")'), ',') + let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') + let s:prtp = &rtp + + if !empty(s:first_rtp) + execute 'set rtp^='.s:first_rtp + execute 'set rtp+='.s:last_rtp + endif +endfunction + +function! s:doautocmd(...) + if exists('#'.join(a:000, '#')) + execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) + endif +endfunction + +function! s:dobufread(names) + for name in a:names + let path = s:rtp(g:plugs[name]) + for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] + if len(finddir(dir, path)) + if exists('#BufRead') + doautocmd BufRead + endif + return + endif + endfor + endfor +endfunction + +function! plug#load(...) + if a:0 == 0 + return s:err('Argument missing: plugin name(s) required') + endif + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 + let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') + if !empty(unknowns) + let s = len(unknowns) > 1 ? 's' : '' + return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) + end + let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') + if !empty(unloaded) + for name in unloaded + call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + endfor + call s:dobufread(unloaded) + return 1 + end + return 0 +endfunction + +function! s:remove_triggers(name) + if !has_key(s:triggers, a:name) + return + endif + for cmd in s:triggers[a:name].cmd + execute 'silent! delc' cmd + endfor + for map in s:triggers[a:name].map + execute 'silent! unmap' map + execute 'silent! iunmap' map + endfor + call remove(s:triggers, a:name) +endfunction + +function! s:lod(names, types, ...) + for name in a:names + call s:remove_triggers(name) + let s:loaded[name] = 1 + endfor + call s:reorg_rtp() + + for name in a:names + let rtp = s:rtp(g:plugs[name]) + for dir in a:types + call s:source(rtp, dir.'/**/*.vim') + endfor + if a:0 + if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) + execute 'runtime' a:1 + endif + call s:source(rtp, a:2) + endif + call s:doautocmd('User', name) + endfor +endfunction + +function! s:lod_ft(pat, names) + let syn = 'syntax/'.a:pat.'.vim' + call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) + execute 'autocmd! PlugLOD FileType' a:pat + call s:doautocmd('filetypeplugin', 'FileType') + call s:doautocmd('filetypeindent', 'FileType') +endfunction + +function! s:lod_cmd(cmd, bang, l1, l2, args, names) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) +endfunction + +function! s:lod_map(map, names, with_prefix, prefix) + call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) + call s:dobufread(a:names) + let extra = '' + while 1 + let c = getchar(0) + if c == 0 + break + endif + let extra .= nr2char(c) + endwhile + + if a:with_prefix + let prefix = v:count ? v:count : '' + let prefix .= '"'.v:register.a:prefix + if mode(1) == 'no' + if v:operator == 'c' + let prefix = "\" . 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', s:plug_fnamemodify(repo, ':t:s?\.git$??')) + let spec = extend(s:infer_properties(name, repo), opts) + if !has_key(g:plugs, name) + call add(g:plugs_order, name) + endif + let g:plugs[name] = spec + let s:loaded[name] = get(s:loaded, name, 0) + catch + return s:err(repo . ' ' . v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' + if type == s:TYPE.string + if empty(a:arg) + throw printf(opt_errfmt, 'tag', 'string') + endif + let opts.tag = a:arg + elseif type == s:TYPE.dict + for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] + if has_key(a:arg, opt) + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string') + endif + endfor + for opt in ['on', 'for'] + if has_key(a:arg, opt) + \ && type(a:arg[opt]) != s:TYPE.list + \ && (type(a:arg[opt]) != s:TYPE.string || empty(a:arg[opt])) + throw printf(opt_errfmt, opt, 'string or list') + endif + endfor + if has_key(a:arg, 'do') + \ && type(a:arg.do) != s:TYPE.funcref + \ && (type(a:arg.do) != s:TYPE.string || empty(a:arg.do)) + throw printf(opt_errfmt, 'do', 'string or funcref') + endif + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-\{1}\ / + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn match plugH2 /^-\{2,}/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap 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(s:plug_getcwd()) + throw 'Invalid current working directory. Cannot proceed.' + endif + + for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] + if exists(evar) + throw evar.' detected. Cannot proceed.' + endif + endfor + + call s:job_abort() + if s:switch_in() + if b:plug_preview == 1 + pc + endif + enew + else + call s:new_window() + endif + + nnoremap q :call close_pane() + 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 + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:close_pane() + if b:plug_preview == 1 + pc + let b:plug_preview = -1 + else + bd + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win + set shell=sh + endif + if a:swap + if &shell =~# 'powershell\(\.exe\)\?$' || &shell =~# 'pwsh$' + let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' + elseif &shell =~# 'sh' || &shell =~# 'cmd\(\.exe\)\?$' + set shellredir=>%s\ 2>&1 + endif + endif + return prev +endfunction + +function! s:bang(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(a:0) + " FIXME: Escaping is incomplete. We could use shellescape with eval, + " but it won't work on Windows. + let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') + execute "normal! :execute g:_plug_bang\\" + finally + unlet g:_plug_bang + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + return v:shell_error ? 'Exit status: ' . v:shell_error : '' +endfunction + +function! s:regress_bar() + let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') + call s:progress_bar(2, bar, len(bar)) +endfunction + +function! s:is_updated(dir) + return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) +endfunction + +function! s:do(pull, force, todo) + for [name, spec] in items(a:todo) + if !isdirectory(spec.dir) + continue + endif + let installed = has_key(s:update.new, name) + let updated = installed ? 0 : + \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) + if a:force || installed || updated + execute 'cd' s:esc(spec.dir) + call append(3, '- Post-update hook for '. name .' ... ') + let error = '' + let type = type(spec.do) + if type == s:TYPE.string + if spec.do[0] == ':' + if !get(s:loaded, name, 0) + let s:loaded[name] = 1 + call s:reorg_rtp() + endif + call s:load_plugin(spec) + try + execute spec.do[1:] + catch + let error = v:exception + endtry + if !s:plug_window_exists() + cd - + throw 'Warning: vim-plug was terminated by the post-update hook of '.name + endif + else + let error = s:bang(spec.do) + endif + elseif type == s:TYPE.funcref + try + call s:load_plugin(spec) + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:git_revision(a:spec.dir) + if !empty(output) && !s:hash_match(sha, s:lines(output)[0]) + let credential_helper = s:git_version_requirement(2) ? '-c credential.helper= ' : '' + let output = s:system( + \ 'git '.credential_helper.'fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = [] + if get(g:, 'plug_shallow', 1) + call extend(s:clone_opt, ['--depth', '1']) + if s:git_version_requirement(1, 7, 10) + call add(s:clone_opt, '--no-single-branch') + endif + endif + + if has('win32unix') || has('wsl') + call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = s:git_origin_branch(spec) + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return (a:event == 'stdout' || a:event == 'stderr') ? + \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : + \ s:job_cb('s:job_exit_cb', self, 0, a:data) +endfunction + +function! s:spawn(name, cmd, opts) + let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], + \ 'new': get(a:opts, 'new', 0) } + let s:jobs[a:name] = job + + if s:nvim + if has_key(a:opts, 'dir') + let job.cwd = a:opts.dir + endif + let argv = a:cmd + call extend(job, { + \ 'on_stdout': function('s:nvim_cb'), + \ 'on_stderr': function('s:nvim_cb'), + \ 'on_exit': function('s:nvim_cb'), + \ }) + let jid = s:plug_call('jobstart', argv, job) + if jid > 0 + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = [jid < 0 ? argv[0].' is not executable' : + \ 'Invalid arguments (or job table is full)'] + endif + elseif s:vim8 + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) + if has_key(a:opts, 'dir') + let cmd = s:with_cd(cmd, a:opts.dir, 0) + endif + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + let jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'err_mode': 'raw', + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + call remove(s:jobs, a:name) +endfunction + +function! s:bar() + if s:switch_in() + let total = len(s:update.all) + call setline(1, (s:update.pull ? 'Updating' : 'Installing'). + \ ' plugins ('.len(s:update.bar).'/'.total.')') + call s:progress_bar(2, s:update.bar, total) + call s:switch_out() + endif +endfunction + +function! s:logpos(name) + let max = line('$') + for i in range(4, max > 4 ? max : 4) + if getline(i) =~# '^[-+x*] '.a:name.':' + for j in range(i + 1, max > 5 ? max : 5) + if getline(j) !~ '^ ' + return [i, j - 1] + endif + endfor + return [i, i] + endif + endfor + return [0, 0] +endfunction + +function! s:log(bullet, name, lines) + if s:switch_in() + let [b, e] = s:logpos(a:name) + if b > 0 + silent execute printf('%d,%d d _', b, e) + if b > winheight('.') + let b = 4 + endif + else + let b = 4 + endif + " FIXME For some reason, nomodifiable is set after :d in vim8 + setlocal modifiable + call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) + call s:switch_out() + endif +endfunction + +function! s:update_vim() + let s:jobs = {} + + call s:bar() + call s:tick() +endfunction + +function! s:tick() + let pull = s:update.pull + let prog = s:progress_opt(s:nvim || s:vim8) +while 1 " Without TCO, Vim stack is bound to explode + if empty(s:update.todo) + if empty(s:jobs) && !s:update.fin + call s:update_finish() + let s:update.fin = 1 + endif + return + endif + + let name = keys(s:update.todo)[0] + let spec = remove(s:update.todo, name) + let new = empty(globpath(spec.dir, '.git', 1)) + + call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') + redraw + + let has_tag = has_key(spec, 'tag') + if !new + let [error, _] = s:git_validate(spec, 0) + if empty(error) + if pull + let cmd = s:git_version_requirement(2) ? ['git', '-c', 'credential.helper=', 'fetch'] : ['git', 'fetch'] + if has_tag && !empty(globpath(spec.dir, '.git/shallow')) + call extend(cmd, ['--depth', '99999999']) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, cmd, { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + let cmd = ['git', 'clone'] + if !has_tag + call extend(cmd, s:clone_opt) + endif + if !empty(prog) + call add(cmd, prog) + endif + call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt').join(' ') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, script) + let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') + return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') +endfunction + +function! s:shellesc_ps1(arg) + return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" +endfunction + +function! s:shellesc_sh(arg) + return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" +endfunction + +" Escape the shell argument based on the shell. +" Vim and Neovim's shellescape() are insufficient. +" 1. shellslash determines whether to use single/double quotes. +" Double-quote escaping is fragile for cmd.exe. +" 2. It does not work for powershell. +" 3. It does not work for *sh shells if the command is executed +" via cmd.exe (ie. cmd.exe /c sh -c command command_args) +" 4. It does not support batchfile syntax. +" +" Accepts an optional dictionary with the following keys: +" - shell: same as Vim/Neovim 'shell' option. +" If unset, fallback to 'cmd.exe' on Windows or 'sh'. +" - script: If truthy and shell is cmd.exe, escape for batchfile syntax. +function! plug#shellescape(arg, ...) + if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' + return a:arg + endif + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\(\.exe\)\?$' + return s:shellesc_cmd(a:arg, script) + elseif shell =~# 'powershell\(\.exe\)\?$' || shell =~# 'pwsh$' + return s:shellesc_ps1(a:arg) + endif + return s:shellesc_sh(a:arg) +endfunction + +function! s:glob_dir(path) + return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') +endfunction + +function! s:progress_bar(line, bar, total) + call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') +endfunction + +function! s:compare_git_uri(a, b) + " See `git help clone' + " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] + " [git@] github.com[:port] : junegunn/vim-plug [.git] + " file:// / junegunn/vim-plug [/] + " / junegunn/vim-plug [/] + let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' + let ma = matchlist(a:a, pat) + let mb = matchlist(a:b, pat) + return ma[1:2] ==# mb[1:2] +endfunction + +function! s:format_message(bullet, name, message) + if a:bullet != 'x' + return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] + else + let lines = map(s:lines(a:message), '" ".v:val') + return extend([printf('x %s:', a:name)], lines) + endif +endfunction + +function! s:with_cd(cmd, dir, ...) + let script = a:0 > 0 ? a:1 : 1 + return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) +endfunction + +function! s:system(cmd, ...) + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + if type(a:cmd) == s:TYPE.list + " Neovim's system() supports list argument to bypass the shell + " but it cannot set the working directory for the command. + " Assume that the command does not rely on the shell. + if has('nvim') && a:0 == 0 + return system(a:cmd) + endif + let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) + if &shell =~# 'powershell\(\.exe\)\?$' + let cmd = '& ' . cmd + endif + else + let cmd = a:cmd + endif + if a:0 > 0 + let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) + endif + if s:is_win && type(a:cmd) != s:TYPE.list + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = [s:git_local_branch(a:spec.dir), s:git_origin_url(a:spec.dir)] + let remote = result[-1] + if empty(remote) + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let sha = s:git_revision(a:spec.dir) + if empty(sha) + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let current_branch = result[0] + " Check tag + let origin_branch = s:git_origin_branch(a:spec) + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif origin_branch !=# current_branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ current_branch, origin_branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system([ + \ 'git', 'rev-list', '--count', '--left-right', + \ printf('HEAD...origin/%s', origin_branch) + \ ], a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', origin_branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ origin_branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + return s:system(s:is_win + \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) + \ : ['rm', '-rf', a:dir]) + endif +endfunction + +function! s:clean(force) + call s:prepare() + call append(0, 'Searching for invalid plugins in '.g:plug_home) + call append(1, '') + + " List of valid directories + let dirs = [] + let errs = {} + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + if !s:is_managed(name) + call add(dirs, spec.dir) + else + let [err, clean] = s:git_validate(spec, 1) + if clean + let errs[spec.dir] = s:lines(err)[0] + else + call add(dirs, spec.dir) + endif + endif + let cnt += 1 + call s:progress_bar(2, repeat('=', cnt), total) + normal! 2G + redraw + endfor + + let allowed = {} + for dir in dirs + let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 + let allowed[dir] = 1 + for child in s:glob_dir(dir) + let allowed[child] = 1 + endfor + endfor + + let todo = [] + let found = sort(s:glob_dir(g:plug_home)) + while !empty(found) + let f = remove(found, 0) + if !has_key(allowed, f) && isdirectory(f) + call add(todo, f) + call append(line('$'), '- ' . f) + if has_key(errs, f) + call append(line('$'), ' ' . errs[f]) + endif + let found = filter(found, 'stridx(v:val, f) != 0') + end + endwhile + + 4 + redraw + if empty(todo) + call append(line('$'), 'Already clean.') + else + let s:clean_count = 0 + call append(3, ['Directories to delete:', '']) + redraw! + if a:force || s:ask_no_interrupt('Delete all directories?') + call s:delete([6, line('$')], 1) + else + call setline(4, 'Cancelled.') + nnoremap 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 + let err_count = 0 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + let err = s:rm_rf(line[2:]) + setlocal modifiable + if empty(err) + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + else + delete _ + call append(l1 - 1, s:format_message('x', line[1:], err)) + let l2 += len(s:lines(err)) + let err_count += 1 + endif + let msg = printf('Removed %d directories.', s:clean_count) + if err_count > 0 + let msg .= printf(' Failed to remove %d directories.', err_count) + endif + call setline(4, msg) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) + if v:shell_error + return s:err('Error upgrading vim-plug: '. out) + endif + + if readfile(s:me) ==# readfile(new) + echo 'vim-plug is already up-to-date' + return 0 + else + call rename(s:me, s:me . '.old') + call rename(new, s:me) + unlet g:loaded_plug + echo 'vim-plug has been upgraded' + return 1 + endif + finally + silent! call s:rm_rf(tmp) + endtry +endfunction + +function! s:upgrade_specs() + for spec in values(g:plugs) + let spec.frozen = get(spec, 'frozen', 0) + endfor +endfunction + +function! s:status() + call s:prepare() + call append(0, 'Checking plugins') + call append(1, '') + + let ecnt = 0 + let unloaded = 0 + let [cnt, total] = [0, len(g:plugs)] + for [name, spec] in items(g:plugs) + let is_dir = isdirectory(spec.dir) + if has_key(spec, 'uri') + if is_dir + let [err, _] = s:git_validate(spec, 1) + let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] + else + let [valid, msg] = [0, 'Not found. Try PlugInstall.'] + endif + else + if is_dir + let [valid, msg] = [1, 'OK'] + else + let [valid, msg] = [0, 'Not found.'] + endif + endif + let cnt += 1 + let ecnt += !valid + " `s:loaded` entry can be missing if PlugUpgraded + if is_dir && get(s:loaded, name, -1) == 0 + let unloaded = 1 + let msg .= ' (not loaded)' + endif + call s:progress_bar(2, repeat('=', cnt), total) + call append(3, s:format_message(valid ? '-' : 'x', name, msg)) + normal! 2G + redraw + endfor + call setline(1, 'Finished. '.ecnt.' error(s).') + normal! gg + setlocal nomodifiable + if unloaded + echo "Press 'L' on each line to load plugin, or 'U' to update" + nnoremap 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 + let batchfile = '' + try + let [sh, shellcmdflag, shrd] = s:chsh(1) + let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + execute 'silent %!' cmd + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry + setlocal nomodifiable + nnoremap 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 branch = s:git_origin_branch(v) + if len(branch) + let range = origin ? '..origin/'.branch : 'HEAD@{1}..' + let cmd = ['git', 'log', '--graph', '--color=never'] + if s:git_version_requirement(2, 10, 0) + call add(cmd, '--no-show-signature') + endif + call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) + if has_key(v, 'rtp') + call extend(cmd, ['--', v.rtp]) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap (plug-preview) :silent! call preview_commit() + if empty(maparg("\", 'n')) + nmap (plug-preview) + endif + if empty(maparg('o', 'n')) + nmap o (plug-preview) + endif + endif + if cnts[0] + nnoremap X :call revert() + echo "Press 'X' on each block to revert the update" + endif + normal! gg + setlocal nomodifiable +endfunction + +function! s:revert() + if search('^Pending updates', 'bnW') + return + endif + + let name = s:find_name(line('.')) + if empty(name) || !has_key(g:plugs, name) || + \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' + return + endif + + call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) + setlocal modifiable + normal! "_dap + setlocal nomodifiable + echo 'Reverted' +endfunction + +function! s:snapshot(force, ...) abort + call s:prepare() + setf vim + call append(0, ['" Generated by vim-plug', + \ '" '.strftime("%c"), + \ '" :source this file in vim to restore the snapshot', + \ '" or execute: vim -S snapshot.vim', + \ '', '', 'PlugUpdate!']) + 1 + let anchor = line('$') - 3 + let names = sort(keys(filter(copy(g:plugs), + \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) + for name in reverse(names) + let sha = s:git_revision(g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@= 0.1.5 +if (has("termguicolors")) + set termguicolors +endif + +" Theme +colorscheme dracula " OceanicNext +set background=dark +"""""""""""" + +" allow backspacing over everything in insert mode +set backspace=indent,eol,start + +if has("vms") + set nobackup " do not keep a backup file, use versions instead +else + set backup " keep a backup file +endif + +set undodir=~/.config/nvim/undodir +set undofile +set undolevels=100 +set undoreload=1000 + +set backupdir=~/.config/nvim/backup +set directory=~/.config/nvim/backup + +set ruler " show the cursor position all the time +set cursorline + +set showcmd " display incomplete commands + +if has('mouse') + set mouse=a +endif + +if has("autocmd") + + augroup vimrcEx + au! + + " For all text files set 'textwidth' to 78 characters. + autocmd FileType text setlocal textwidth=108 + + " Trim whitespace onsave + autocmd BufWritePre * %s/\s\+$//e + + " When editing a file, always jump to the last known cursor position. + " Don't do it when the position is invalid or when inside an event handler + " (happens when dropping a file on gvim). + " Also don't do it when the mark is in the first line, that is the default + " position when opening a file. + autocmd BufReadPost * + \ if line("'\"") > 1 && line("'\"") <= line("$") | + \ exe "normal! g`\"" | + \ endif + + augroup END + +endif " has("autocmd") + +" tab stuff +set tabstop=2 +set softtabstop=2 +set expandtab +set smarttab +set shiftwidth=2 +set autoindent +set smartindent + +set complete-=i + +set nrformats-=octal + +set ttimeout +set ttimeoutlen=100 + +" detect .md as markdown instead of modula-2 +autocmd BufNewFile,BufReadPost *.md set filetype=markdown + +" Unix as standard file type +set ffs=unix,dos,mac + +" Always utf8 +set termencoding=utf-8 +set encoding=utf-8 +set fileencoding=utf-8 + +set so=5 " scroll lines above/below cursor +set sidescrolloff=5 +set lazyredraw + +set magic " for regular expressions + +if &listchars ==# 'eol:$' + set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ +endif + +if has('path_extra') + setglobal tags-=./tags tags^=./tags; +endif + +set autoread + +if &history < 1000 + set history=1000 +endif +if &tabpagemax < 50 + set tabpagemax=50 +endif +if !empty(&viminfo) + set viminfo^=! +endif +set sessionoptions-=options + +" buffer settings +set hid " buffer becomes hidden when abandoned + +" stop highlighting of underscores in markdown files +autocmd BufNewFile,BufRead,BufEnter *.md,*.markdown :syntax match markdownIgnore "_" + +" clipboard " salam:x +if (executable('pbcopy') || executable('xclip') || executable('xsel')) && has('clipboard') + set clipboard=unnamed +endif +" set clipboard=unnamedplus " salam diff --git a/nvim/config/init.vimrc b/nvim/config/init.vimrc new file mode 100644 index 0000000..3cbb2be --- /dev/null +++ b/nvim/config/init.vimrc @@ -0,0 +1,81 @@ +call plug#begin('~/.config/nvim/plugged') + +" for sort these lines +" :'<,'>sort + +" general +Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } " dark powered neo-completion +Plug 'SirVer/ultisnips' " completion ~~ UltiSnips is the ultimate solution for snippets in Vim +Plug 'benekastah/neomake' " asynchronously runs programs job-control functionality +Plug 'christoomey/vim-tmux-navigator' " navigate from vim to tmux windows +Plug 'ervandew/supertab' " super tab +Plug 'haya14busa/incsearch.vim' " search example: /salam +Plug 'honza/vim-snippets' " vim snippets +Plug 'jaawerth/neomake-local-eslint-first' " for eslint local +Plug 'kien/ctrlp.vim' " quick look & open file +Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } " nerd tree +Plug 'sheerun/vim-polyglot' " A collection of language packs for Vim. +Plug 'terryma/vim-multiple-cursors' " Ctrl n multi cursor + +" editing +Plug 'Raimondi/delimitMate' " automatic closing of quotes, parenthesis, brackets, etc. +Plug 'airblade/vim-gitgutter' " shows a git diff in the 'gutter' +Plug 'junegunn/vim-easy-align' " indent multi text ( easy align : so many feature ), eg: `gaip* ` `v2j*:` +Plug 'mbbill/undotree' " undo tree +Plug 'nathanaelkane/vim-indent-guides' " `,ig` to toggle +Plug 'tpope/vim-commentary' " for multiline became to comment lines +Plug 'tpope/vim-repeat' " Repeat.vim remaps . in a way that plugins can tap into it +Plug 'tpope/vim-speeddating' " a Ctrl-a/Ctrl-x command to increment / decrement a line of Date +Plug 'tpope/vim-surround' " ysiw' | ds{ds) provides mappings to easily delete, change and add such surroundings in pairs +Plug 'tpope/vim-unimpaired' " [a cursor to previous a, ]x cursor to next x +Plug 'vim-scripts/camelcasemotion' " when w,e,b help cursor stay at camelCaseWord + +" eye candy +Plug 'lilydjwg/colorizer', { 'on': 'ColorToggle' } " #aaa to Colorize +Plug 'myusuf3/numbers.vim' " intelligently toggling line numbers +Plug 'vim-airline/vim-airline' " Lean & mean status/tabline for vim that's light as air +Plug 'vim-airline/vim-airline-themes' " airline theme... + +" colorschemes +Plug 'dracula/vim', { 'as': 'dracula' } " favorite theme +Plug 'ryanoasis/vim-devicons' " look at beauty icons + +" text objects +Plug 'glts/vim-textobj-comment' " provides text objects for comments , eg. SHIFT + \" +Plug 'kana/vim-textobj-fold' " provides text objects for fold , eg. yaz to yank arround fold +Plug 'kana/vim-textobj-function' " Text objects for functions, build error ??! +Plug 'kana/vim-textobj-indent' " Text objects for indented blocks of lines, eg. y- yank of blocks of lines +Plug 'kana/vim-textobj-user' " create your own text objects without pain, eg. call textobj#user#plugin('datetime', { ... +Plug 'wellle/targets.vim' " ci' to (cut in single quote), also support (I l L a A n N) for (In Last last in .. next Next) + +" javascript +Plug 'isRuslan/vim-es6' " Write JavaScript ES6 easily with vim. (You need SnipMate or UltiSnips installed. ??) +Plug 'leafgarland/typescript-vim' " just for typescript +Plug 'mhartington/vim-angular2-snippets' " for TS and ng2 (version 4.o.o >=) +Plug 'moll/vim-node' " gf to jump to source and module files, :Nedit {module_name} to edit the main file of a module +Plug 'mxw/vim-jsx', { 'for': ['javascript', 'javascript.jsx'] } " JSX (React) syntax highlighting and indenting for vim +Plug 'othree/javascript-libraries-syntax.vim' " Supports JavaScript libraries . Should work well with other (ng, react, vue, coffeScript, typeScript...) +Plug 'othree/yajs.vim' " Another JavaScript Syntax file for Vim. Key differences +Plug 'pangloss/vim-javascript', { 'for': ['javascript', 'javascript.jsx', 'vue', 'vue.html.javascript.css'] } " rovides syntax highlighting and improved indentation +Plug 'posva/vim-vue' + +" golang +" Require: go get -u github.com/nsf/gocode +Plug 'fatih/vim-go' , { 'do': ':GoInstallBinaries' } " GoUpdateBinaries will take a long time + +" python +Plug 'python-mode/python-mode', { 'for': 'python', 'branch': 'develop' } + +" html +Plug 'mattn/emmet-vim' " provides support for expanding abbreviations similar to emmet +Plug 'othree/html5.vim' " HTML5 + inline SVG omnicomplete function, indent and syntax for Vim. Based on the default htmlcomplete.vim, This plugin contributes to vim-polyglot language pack. + +" css +Plug 'hail2u/vim-css3-syntax' " CSS3 syntax (and syntax defined in some foreign specifications) support for Vim’s built-in +Plug 'othree/csscomplete.vim' " Update the bult-in CSS complete function to latest CSS standard. +Plug 'wavded/vim-stylus' " for stylus + +" json +Plug 'elzr/vim-json' " distinct highlighting of keywords vs values, JSON-specific (non-JS) warnings, quote concealing. Pathogen-friendly, support in polyglot + +call plug#end() diff --git a/nvim/config/keys.vimrc b/nvim/config/keys.vimrc new file mode 100644 index 0000000..696064d --- /dev/null +++ b/nvim/config/keys.vimrc @@ -0,0 +1,136 @@ +" map Leader +let mapleader = "," + +" keep backward f search, remapping it to ,; +nnoremap ; , + +" buffer keys +nnoremap bb :b# +nnoremap n :bn +nnoremap q :bp +nnoremap bf :bf +nnoremap bl :bl +nnoremap bw :w:bd +nnoremap d :bd! +" new buffer/tab +nnoremap e :enew + +" window keys +nnoremap w< < +nnoremap w> > +nnoremap w- - +nnoremap w+ + +nnoremap s :split +nnoremap v :vsplit +nnoremap wx :close + +" for commentary +nnoremap " :Commentary +vnoremap " :Commentary +" :7,17Commentary + +" multiline moving +" ∆ +" ˚ +nnoremap ∆ :m .+1== +nnoremap ˚ :m .-2== +vnoremap ∆ :m '>+1gv=gv +vnoremap ˚ :m '<-2gv=gv +inoremap ∆ :m .+1==gi +inoremap ˚ :m .-2==gi + +" command mode maps +" better command-line window scrolling with & +" cnoremap +" cnoremap + +" %% to expand active buffer location on cmdline +cnoremap %% getcmdtype() == ':' ? expand('%:h').'/' : '%%' + +" CtrlP keys +nnoremap pp :CtrlP +nnoremap pm :CtrlPMRUFiles +nnoremap pb :CtrlPBuffer + +" Function keys +nnoremap :let _s=@/:%s/\s\+$//e:let @/=_s:nohl +nnoremap :NERDTreeToggle +nnoremap :source $HOME/.config/nvim/init.vim +nnoremap :set hlsearch! +nnoremap :UndotreeToggle +" nnoremap :Geeknote +" indent whole file according to syntax rules +" noremap gg=G + +" CTRL-U in insert mode deletes a lot. Use CTRL-G u to first break undo, +" so that you can undo CTRL-U after inserting a line break. +inoremap u +" Don't use Ex mode, use Q for formatting +map Q gq + +" relative line numbers +nnoremap 3 :NumbersToggle + +" snippets +let g:UltiSnipsExpandTrigger="" + +" remap number increment to C-s (C-a is already in use by tmux) +nmap + +" Word count selection +vnoremap w :w !wc -w + +" vim paste mode toggle (for fixing indentation issues when pasting text) +nnoremap :set invpaste paste? +set pastetoggle= +set showmode + +" override read-only permissions +cmap w!! %!sudo tee > /dev/null % + +" allow ,, for vimsneak +nmap , SneakPrevious + +" camelCase motion settings +map w CamelCaseMotion_w +map b CamelCaseMotion_b +map e CamelCaseMotion_e +sunmap w +sunmap b +sunmap e + +" start interactive EasyAlign in visual mode (e.g. vip) +vmap (EasyAlign) + +" start interactive EasyAlign for a motion/text object (e.g. gaip) +nmap ga (EasyAlign) + +" neomake +nmap o :lopen +nmap c :lclose +nmap , :ll +nmap n :lnext +nmap p :lprev + +" folding +nmap f zf% +" Folding {{{ +set foldenable " enable folding +set foldlevelstart=2 " start folding then we are 10 blocks deep +set foldnestmax=5 " 10 nested fold max +set foldmethod=indent " fold based on indent level +"}}} + +" deoplete tab-complete +inoremap pumvisible() ? "\" : deoplete#mappings#manual_complete() +" , for regular tab +inoremap + +" tern +autocmd FileType javascript nnoremap gb :TernDef + +" colorizer +nmap tc :ColorToggle + +" for vnoremap selection search +vnoremap / y/" diff --git a/nvim/config/line.vimrc b/nvim/config/line.vimrc new file mode 100644 index 0000000..040f536 --- /dev/null +++ b/nvim/config/line.vimrc @@ -0,0 +1,53 @@ +" All status line configuration goes here + +set cmdheight=1 +set display+=lastline + +" general config +set laststatus=2 " always show status line +set showtabline=2 " always show tabline +set noshowmode " hide default mode text (e.g. INSERT) as airline already displays it + +" airline config +let g:airline_powerline_fonts=1 +let g:airline#extensions#tabline#enabled=1 " buffers at the top as tabs +let g:airline#extensions#tabline#show_tabs=0 +let g:airline#extensions#tabline#show_tab_type=1 +let g:airline#extensions#tmuxline#enabled=0 +let g:airline_theme = 'lucius' +if !exists('g:airline_symbols') + let g:airline_symbols = {} +endif + +let g:airline_symbols.linenr = '' +let g:airline_symbols.paste = 'ρ' +let g:airline_symbols.readonly = '' + +let g:airline#extensions#quickfix#quickfix_text = 'QF' +let g:airline#extensions#quickfix#location_text = 'LL' + +" disable unused extensions (performance) +let g:airline#extensions#bufferline#enabled = 0 +let g:airline#extensions#capslock#enabled = 0 +let g:airline#extensions#csv#enabled = 0 +let g:airline#extensions#ctrlspace#enabled = 0 +let g:airline#extensions#eclim#enabled = 0 +let g:airline#extensions#hunks#enabled = 0 +let g:airline#extensions#nrrwrgn#enabled = 0 +let g:airline#extensions#promptline#enabled = 0 +let g:airline#extensions#syntastic#enabled = 0 +let g:airline#extensions#taboo#enabled = 0 +let g:airline#extensions#tagbar#enabled = 0 +let g:airline#extensions#virtualenv#enabled = 0 +let g:airline#extensions#whitespace#enabled = 0 + +" tmuxline config +let g:tmuxline_preset = { + \ 'a': '#S', + \ 'b': '#F', + \ 'c': '#W', + \ 'win': ['#I', '#W'], + \ 'cwin': ['#I', '#W'], + \ 'x': '#h', + \ 'y': '%b %d', + \ 'z': '%R'} diff --git a/nvim/config/plugins.vimrc b/nvim/config/plugins.vimrc new file mode 100644 index 0000000..a07268a --- /dev/null +++ b/nvim/config/plugins.vimrc @@ -0,0 +1,127 @@ +filetype plugin indent on + +" deoplete + +let g:deoplete#enable_at_startup = 1 +let g:deoplete#disable_auto_complete = 1 +autocmd InsertLeave,CompleteDone * if pumvisible() == 0 | pclose | endif +let g:deoplete#omni_input_patterns = {} +" if !exists('g:deoplete#omni#input_patterns') +" let g:deoplete#omni#input_patterns = {} +" endif + +if system('uname -s') == "Darwin\n" + " OSX + let g:python3_host_prog = '/opt/homebrew/Caskroom/miniforge/base/bin/python3' + " /usr/local/bin/python3 +else + " Linux + let g:python3_host_prog = '/usr/bin/python3' +endif + +" Linux & (new) OSX +" let g:python3_host_prog = '/usr/bin/python3' + +" neomake config +autocmd! BufWritePost * Neomake +" autocmd BufLeave * QFix + +let g:neomake_warning_sign = { + \ 'text': 'W', + \ 'texthl': 'WarningMsg', + \ } + +let g:neomake_error_sign = { + \ 'text': 'E', + \ 'texthl': 'ErrorMsg', + \ } + +let g:neomake_list_height = 3 +let g:neomake_open_list = 2 +let g:neomake_verbose = 2 + +let b:neomake_javascript_eslint_exe = nrun#Which('eslint') " for find local eslint +let g:neomake_javascript_enabled_makers = ['eslint'] +if exists(':Neomake') + " Check for lint errors on open & write + autocmd BufRead,BufWritePost,BufEnter *.js,*.jsx,*.vue silent! Neomake standard|redraw +endif + +" make background transparent +hi Normal ctermbg=NONE +hi EndOfBuffer ctermbg=NONE +hi LineNr ctermbg=234 + +" CtrlP +let g:ctrlp_prompt_mappings={'PrtClearCache()':['']} +let g:ctrlp_prompt_mappings={'PrtdeleteEnt()':['']} +let g:ctrlp_match_window='bottom,order:btt,min:2,max:15' +set wildmenu " enhanced autocomplete +set wildignore+=*/tmp/*,*/node_modules/*,*/.git/*,*.so,*.swp,*.zip,*node_modules*,*.jpg,*.png,*.svg,*.ttf,*.woff,*.woff3,*.eot +",*public/css/*,*public/js* +" let g:ctrlp_user_command = 'find %s -type f' + +" delimitMate options +let delimitMate_expand_cr=1 + +" enable matchit (for matching tags with %) +runtime macros/matchit.vim + +" vim-sneak settings +hi SneakPluginTarget ctermfg=black ctermbg=181818 + +" javascript libraries syntax +let g:used_javascript_libs = 'vue' " underscore,react,chai, more to see -> /othree/javascript-libraries-syntax.vim + +" tern +if exists('g:plugs["tern_for_vim"]') + let g:tern_show_argument_hints = 'on_hold' + let g:tern_show_signature_in_pum = 1 + + autocmd FileType javascript setlocal omnifunc=tern#Complete +endif + +" disable colorizer at startup +let g:colorizer_startup = 0 +let g:colorizer_nomap = 1 + +" " ale (eslint) +" set nocompatible +" filetype off +" let &runtimepath.=',~/.config/nvim/plugged/ale' +" filetype plugin on +" silent! helptags ALL + +" vim-devicons +set guifont=Droid\ Sans\ Mono\ for\ Powerline\ Plus\ Nerd\ File\ Types:h11 +let g:airline_powerline_fonts = 1 + +" NERDTree +let g:NERDTreeWinPos = 'right' + +" vim-json +hi! def link jsonKeyword Identifier +let g:vim_json_syntax_conceal = 1 + +" javascript +autocmd BufRead,BufNewFile *.js setlocal filetype=javascript + +" vue +autocmd BufRead,BufNewFile *.vue setlocal filetype=vue.html.javascript.css +" let g:vue_disable_pre_processors = 1 + +" For php tab space +autocmd Filetype php setlocal ts=4 sw=4 sts=0 expandtab +autocmd Filetype blade setlocal ts=2 sw=2 sts=0 expandtab + +" vim-go +let g:deoplete#sources#go#gocode_binary = $GOPATH.'/bin/gocode' +let g:deoplete#sources#go#sort_class = ['package', 'func', 'type', 'var', 'const'] +autocmd Filetype go setlocal ts=2 sw=2 sts=0 expandtab + +" Python +" pythonmod +let g:pymode_lint = 1 +let g:pymode_python = 'python3' +" fold +autocmd BufReadPost,BufEnter *.py normal zM diff --git a/nvim/init.vim b/nvim/init.vim new file mode 100644 index 0000000..55a1858 --- /dev/null +++ b/nvim/init.vim @@ -0,0 +1,5 @@ +source $HOME/.config/nvim/config/init.vimrc +source $HOME/.config/nvim/config/general.vimrc +source $HOME/.config/nvim/config/plugins.vimrc +source $HOME/.config/nvim/config/keys.vimrc +source $HOME/.config/nvim/config/line.vimrc diff --git a/nvim/scripts/install-in-ubuntu.sh b/nvim/scripts/install-in-ubuntu.sh new file mode 100644 index 0000000..5787b10 --- /dev/null +++ b/nvim/scripts/install-in-ubuntu.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# For Ubuntu 18 +CONF_DIR=~/deps/nvim +mkdir -p ~/deps + +sudo apt install -y software-properties-common +sudo add-apt-repository ppa:neovim-ppa/stable +sudo apt update -y +sudo apt install -y neovim +sudo apt install -y python-dev python-pip python3-dev python3-pip +sudo apt install -y git # for clone +sudo apt autoremove -y + +git clone https://github.com/hiyali/nvim $CONF_DIR +mkdir -p ~/.config +ln -s $CONF_DIR ~/.config/nvim + +sudo update-alternatives --config editor From 842296636871d841bff76f1f10d7d24e39861de2 Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Mon, 22 Aug 2022 10:45:25 -0700 Subject: [PATCH 17/25] zshrc --- zsh/.zshrc | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/zsh/.zshrc b/zsh/.zshrc index 44fabcc..f45e5a3 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -3,39 +3,17 @@ integer t0=$(date '+%s') source $HOME/antigen.zsh +autoload -U promptinit; promptinit +# prompt pure +prompt purity -# Load the oh-my-zsh's library. -antigen use oh-my-zsh - -# Bundles from the default repo (robbyrussell's oh-my-zsh). -antigen bundle git -antigen bundle heroku -antigen bundle pip -antigen bundle docker -antigen bundle kubernetes -antigen bundle lein -antigen bundle fzf -antigen bundle gatsby -antigen bundle httpie -antigen bundle mosh -antigen bundle npm -antigen bundle copyfile -antigen bundle copydir -antigen bundle yarn -antigen bundle alias-finder -antigen bundle command-not-found -antigen bundle zsh-users/zsh-autosuggestions - -# Syntax highlighting bundle. -antigen bundle zsh-users/zsh-syntax-highlighting - -# Load the theme. -antigen theme robbyrussell + +# Load Antigen configurations +antigen init ~/.antigenrc # Tell Antigen that you're done. antigen apply - function { local -i t1 startup t1=$(date '+%s') @@ -44,3 +22,20 @@ function { ##print "startup time: $startup" } unset t0 + +[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh +export PATH="/usr/local/opt/binutils/bin:$PATH" + +# pnpm +export PNPM_HOME="/Users/jon/Library/pnpm" +export PATH="$PNPM_HOME:$PATH" +# pnpm end +# Created by `pipx` on 2022-07-02 11:27:20 +export PATH="$PATH:/Users/jon/.local/bin" + +# bit +export PATH="$PATH:/Users/jon/bin" +# bit end +export PATH=/Users/jon/.meteor:$PATH + +ZSH_THEME="" From 60f3df26c2408eab33d48fe2893532a7c613e50c Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Wed, 24 Aug 2022 05:26:50 -0700 Subject: [PATCH 18/25] update zsh --- zsh/.antigenrc | 11 +++++++++-- zsh/.zshrc | 24 +++++------------------- zsh/install | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/zsh/.antigenrc b/zsh/.antigenrc index 96faad7..7a0277b 100644 --- a/zsh/.antigenrc +++ b/zsh/.antigenrc @@ -1,6 +1,14 @@ # Load oh-my-zsh library +source $HOME/antigen.zsh + antigen use oh-my-zsh +if [[ $OSTYPE == darwin* ]]; then + antigen bundle macos +fi + +antigen bundle therealklanni/purity + # Load bundles from the default repo (oh-my-zsh) antigen bundle git antigen bundle command-not-found @@ -13,7 +21,6 @@ antigen bundle docker antigen bundle kubernetes antigen bundle lein antigen bundle fzf -antigen bundle gatsby antigen bundle httpie antigen bundle mosh antigen bundle npm @@ -32,7 +39,7 @@ antigen bundle zsh-users/zsh-autosuggestions antigen bundle zsh-users/zsh-syntax-highlighting # Select theme -antigen theme denysdovhan/spaceship-prompt +# antigen theme denysdovhan/spaceship-prompt # Tell Antigen that you're done antigen apply diff --git a/zsh/.zshrc b/zsh/.zshrc index f45e5a3..166658a 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -3,39 +3,25 @@ integer t0=$(date '+%s') source $HOME/antigen.zsh -autoload -U promptinit; promptinit -# prompt pure -prompt purity - +autoload -U promptinit; promptinit # Load Antigen configurations antigen init ~/.antigenrc +# prompt pure +# prompt purity -# Tell Antigen that you're done. -antigen apply function { local -i t1 startup t1=$(date '+%s') startup=$(( t1 - t0 )) [[ $startup -gt 1 ]] && print "Hmm, poor shell startup time: $startup" - ##print "startup time: $startup" + #print "startup time: $startup" } unset t0 [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh -export PATH="/usr/local/opt/binutils/bin:$PATH" -# pnpm -export PNPM_HOME="/Users/jon/Library/pnpm" -export PATH="$PNPM_HOME:$PATH" -# pnpm end -# Created by `pipx` on 2022-07-02 11:27:20 -export PATH="$PATH:/Users/jon/.local/bin" +export PATH="/usr/local/opt/binutils/bin:$PATH" -# bit -export PATH="$PATH:/Users/jon/bin" -# bit end export PATH=/Users/jon/.meteor:$PATH - -ZSH_THEME="" diff --git a/zsh/install b/zsh/install index 86d3601..fb32224 100755 --- a/zsh/install +++ b/zsh/install @@ -1,8 +1,35 @@ #!/bin/bash -curl -L git.io/antigen > antigen.zsh +# Switch to zsh + +export REPO="$(git rev-parse --show-toplevel)" + +if [ -n "`$SHELL -c 'echo $ZSH_VERSION'`" ]; then + # assume Zsh + echo "zsh detected" +elif [ -n "`$SHELL -c 'echo $BASH_VERSION'`" ]; then + # assume Bash + sudo chsh -s $(which zsh) $(whoami) +else + echo "Something's not right..." + exit 0 + +fi + + +function init() { + sudo apt-get install zsh + sudo apt-get install zsh-autosuggestions zsh-syntax-highlighting + curl -L git.io/antigen > ~/antigen.zsh + + cp $REPO/zsh/.zshrc $REPO/zsh/.zprofile $REPO/zsh/.zshenv $REPO/zsh/.antigenrc \ + $REPO/zsh/.antigenrc $HOME/ + + git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf + ~/.fzf/install +} + +init -git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf -~/.fzf/install From 404bc8c5281da445c6fd631c864aef7b9b5b6d21 Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Wed, 24 Aug 2022 05:28:35 -0700 Subject: [PATCH 19/25] add zprofile --- zsh/.zprofile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zsh/.zprofile b/zsh/.zprofile index 4e34736..c6363ea 100644 --- a/zsh/.zprofile +++ b/zsh/.zprofile @@ -4,4 +4,7 @@ export CONFIG="$HOME/conf" # Created by `pipx` on 2022-07-02 11:27:20 export PATH="$PATH:/Users/jon/.local/bin" -eval $(/opt/homebrew/bin/brew shellenv) + +if [[ $OSTYPE == 'darwin'* ]]; then + # eval $(/opt/homebrew/bin/brew shellenv) +fi From 345762c2576ea19d57c91c0ca9893c01b32e4d1a Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Tue, 30 Aug 2022 03:50:20 -0700 Subject: [PATCH 20/25] update zsh --- zsh/.zshrc | 4 ++-- zsh/README.md | 13 +++++++++++++ zsh/install | 8 ++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/zsh/.zshrc b/zsh/.zshrc index 166658a..6e0305b 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -2,12 +2,12 @@ # Time the stuff. integer t0=$(date '+%s') -source $HOME/antigen.zsh +source ${HOME}/antigen/antigen.zsh + autoload -U promptinit; promptinit # Load Antigen configurations antigen init ~/.antigenrc -# prompt pure # prompt purity diff --git a/zsh/README.md b/zsh/README.md index 44a586c..f7fd3b9 100644 --- a/zsh/README.md +++ b/zsh/README.md @@ -1,3 +1,16 @@ `$ chmod +x install` `$ ./install` +# Modify pam.d file to change shell if coming from bash +$ sudo vim /etc/pam.d/chsh + +Replace +``` +auth required pam_shells.so +``` + +with + +``` +auth sufficient pam_shells.so +``` diff --git a/zsh/install b/zsh/install index fb32224..9e46cfc 100755 --- a/zsh/install +++ b/zsh/install @@ -16,14 +16,14 @@ else fi - function init() { sudo apt-get install zsh sudo apt-get install zsh-autosuggestions zsh-syntax-highlighting - curl -L git.io/antigen > ~/antigen.zsh - cp $REPO/zsh/.zshrc $REPO/zsh/.zprofile $REPO/zsh/.zshenv $REPO/zsh/.antigenrc \ - $REPO/zsh/.antigenrc $HOME/ + mkdir $HOME/antigen + curl -L git.io/antigen > $HOME/antigen/antigen.zsh + + cp $REPO/zsh/.zshrc $REPO/zsh/.zprofile $REPO/zsh/.zshenv $REPO/zsh/.antigenrc $HOME/ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install From 1c3c28115ad239660a3f97ee58aa0c73e4e9e3e0 Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Fri, 10 Mar 2023 00:19:25 -0800 Subject: [PATCH 21/25] antigen files --- antigen/init.zsh | 98 +++++++++++++++++++++++++++++++++++++++++++++++ bash/bash_profile | 2 + bash/bashrc | 2 + bash/profile | 2 + zsh/.zshrc | 10 +++-- 5 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 antigen/init.zsh diff --git a/antigen/init.zsh b/antigen/init.zsh new file mode 100644 index 0000000..5d125e3 --- /dev/null +++ b/antigen/init.zsh @@ -0,0 +1,98 @@ +#-- START ZCACHE GENERATED FILE +#-- GENERATED: Fri Mar 10 00:14:02 PST 2023 +#-- ANTIGEN v2.2.2 +_antigen () { + local -a _1st_arguments + _1st_arguments=('apply:Load all bundle completions' 'bundle:Install and load the given plugin' 'bundles:Bulk define bundles' 'cleanup:Clean up the clones of repos which are not used by any bundles currently loaded' 'cache-gen:Generate cache' 'init:Load Antigen configuration from file' 'list:List out the currently loaded bundles' 'purge:Remove a cloned bundle from filesystem' 'reset:Clears cache' 'restore:Restore the bundles state as specified in the snapshot' 'revert:Revert the state of all bundles to how they were before the last antigen update' 'selfupdate:Update antigen itself' 'snapshot:Create a snapshot of all the active clones' 'theme:Switch the prompt theme' 'update:Update all bundles' 'use:Load any (supported) zsh pre-packaged framework') + _1st_arguments+=('help:Show this message' 'version:Display Antigen version') + __bundle () { + _arguments '--loc[Path to the location ]' '--url[Path to the repository ]' '--branch[Git branch name]' '--no-local-clone[Do not create a clone]' + } + __list () { + _arguments '--simple[Show only bundle name]' '--short[Show only bundle name and branch]' '--long[Show bundle records]' + } + __cleanup () { + _arguments '--force[Do not ask for confirmation]' + } + _arguments '*:: :->command' + if (( CURRENT == 1 )) + then + _describe -t commands "antigen command" _1st_arguments + return + fi + local -a _command_args + case "$words[1]" in + (bundle) __bundle ;; + (use) compadd "$@" "oh-my-zsh" "prezto" ;; + (cleanup) __cleanup ;; + (update|purge) compadd $(type -f \-antigen-get-bundles &> /dev/null || antigen &> /dev/null; -antigen-get-bundles --simple 2> /dev/null) ;; + (theme) compadd $(type -f \-antigen-get-themes &> /dev/null || antigen &> /dev/null; -antigen-get-themes 2> /dev/null) ;; + (list) __list ;; + esac +} +antigen () { + local MATCH MBEGIN MEND + [[ "$ZSH_EVAL_CONTEXT" =~ "toplevel:*" || "$ZSH_EVAL_CONTEXT" =~ "cmdarg:*" ]] && source "/Users/jon/antigen.zsh" && eval antigen $@; + return 0; +} +typeset -gaU fpath path +fpath+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) path+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) +_antigen_compinit () { + autoload -Uz compinit; compinit -d "/Users/jon/.antigen/.zcompdump"; compdef _antigen antigen + add-zsh-hook -D precmd _antigen_compinit +} +autoload -Uz add-zsh-hook; add-zsh-hook precmd _antigen_compinit +compdef () {} + +if [[ -n "/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh" ]]; then + ZSH="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh"; ZSH_CACHE_DIR="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/cache/" +fi +#--- BUNDLES BEGIN +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh'; +source '/Users/jon/.antigen/bundles/therealklanni/purity/purity.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh'; +source '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm/zsh-nvm.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh'; +source '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases/zsh-docker-compose-aliases.zsh'; +source '/Users/jon/.antigen/bundles/webyneter/docker-aliases/docker-aliases.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-completions/zsh-completions.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh'; + +#--- BUNDLES END +typeset -gaU _ANTIGEN_BUNDLE_RECORD; _ANTIGEN_BUNDLE_RECORD=('https://github.com/robbyrussell/oh-my-zsh.git lib plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/macos plugin true' 'https://github.com/therealklanni/purity.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/git plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/command-not-found plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/docker plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/heroku plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/pip plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/kubernetes plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/lein plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/fzf plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/httpie plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/mosh plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/npm plugin true' 'https://github.com/lukechilds/zsh-nvm.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copyfile plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copypath plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/yarn plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/alias-finder plugin true' 'https://github.com/luismayta/zsh-docker-compose-aliases.git / plugin true' 'https://github.com/webyneter/docker-aliases.git / plugin true' 'https://github.com/zsh-users/zsh-completions.git / plugin true' 'https://github.com/zsh-users/zsh-autosuggestions.git / plugin true' 'https://github.com/zsh-users/zsh-syntax-highlighting.git / plugin true') +typeset -g _ANTIGEN_CACHE_LOADED; _ANTIGEN_CACHE_LOADED=true +typeset -ga _ZCACHE_BUNDLE_SOURCE; _ZCACHE_BUNDLE_SOURCE=('/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh' '/Users/jon/.antigen/bundles/therealklanni/purity//' '/Users/jon/.antigen/bundles/therealklanni/purity///purity.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/kubernetes' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm//' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm///zsh-nvm.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases//' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases///zsh-docker-compose-aliases.zsh' '/Users/jon/.antigen/bundles/webyneter/docker-aliases//' '/Users/jon/.antigen/bundles/webyneter/docker-aliases///docker-aliases.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions///zsh-completions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions///zsh-autosuggestions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting//' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting///zsh-syntax-highlighting.plugin.zsh') +typeset -g _ANTIGEN_CACHE_VERSION; _ANTIGEN_CACHE_VERSION='v2.2.2' + +#-- END ZCACHE GENERATED FILE diff --git a/bash/bash_profile b/bash/bash_profile index 0cf3599..66ddb06 100644 --- a/bash/bash_profile +++ b/bash/bash_profile @@ -35,3 +35,5 @@ export PATH=$PATH:"/mnt/c/Users/telne/AppData/Local/Programs/Microsoft VS Code/b export PATH="$PATH:/home/tlcie/.local/bin" +export VOLTA_HOME="$HOME/.volta" +export PATH="$VOLTA_HOME/bin:$PATH" diff --git a/bash/bashrc b/bash/bashrc index b4a0541..1032d7d 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -61,3 +61,5 @@ export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH" export GOROOT=/usr/local/go export GOPATH=$HOME/go export PATH=$GOPATH/bin:$GOROOT/bin:$PATH +export VOLTA_HOME="$HOME/.volta" +export PATH="$VOLTA_HOME/bin:$PATH" diff --git a/bash/profile b/bash/profile index 6892bee..0d2a068 100644 --- a/bash/profile +++ b/bash/profile @@ -28,3 +28,5 @@ if [ -d "$HOME/.local/bin" ] ; then PATH="$HOME/.local/bin:$PATH" fi +export VOLTA_HOME="$HOME/.volta" +export PATH="$VOLTA_HOME/bin:$PATH" diff --git a/zsh/.zshrc b/zsh/.zshrc index 6e0305b..9956c0e 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -2,12 +2,12 @@ # Time the stuff. integer t0=$(date '+%s') -source ${HOME}/antigen/antigen.zsh - +source $HOME/antigen.zsh autoload -U promptinit; promptinit # Load Antigen configurations antigen init ~/.antigenrc +# prompt pure # prompt purity @@ -21,7 +21,9 @@ function { unset t0 [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh - +export OPENAI_API_KEY='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' export PATH="/usr/local/opt/binutils/bin:$PATH" - export PATH=/Users/jon/.meteor:$PATH +# export PATH="/usr/local/opt/openssl@3/bin:$PATH" +export VOLTA_HOME="$HOME/.volta" +export PATH="$VOLTA_HOME/bin:$PATH" From 96b955d0afee81f1672ba856e09ecbecec35d52e Mon Sep 17 00:00:00 2001 From: Jonathan Lau Date: Tue, 16 Jul 2024 04:43:41 -0700 Subject: [PATCH 22/25] Test antidote and add kitty settings --- kitty/current-theme.conf | 21 + kitty/kitty.conf | 2297 +++++++++++++++++++++++++++++++++ macprefs | 28 + zsh/antidote/.zsh_plugins.txt | 32 + zsh/install | 3 + 5 files changed, 2381 insertions(+) create mode 100644 kitty/current-theme.conf create mode 100644 kitty/kitty.conf create mode 100644 macprefs create mode 100644 zsh/antidote/.zsh_plugins.txt diff --git a/kitty/current-theme.conf b/kitty/current-theme.conf new file mode 100644 index 0000000..346f3dc --- /dev/null +++ b/kitty/current-theme.conf @@ -0,0 +1,21 @@ +background #000000 +foreground #e9e9e9 +cursor #e9e9e9 +selection_background #424242 +color0 #000000 +color8 #000000 +color1 #d44d53 +color9 #d44d53 +color2 #b9c949 +color10 #b9c949 +color3 #e6c446 +color11 #e6c446 +color4 #79a6da +color12 #79a6da +color5 #c396d7 +color13 #c396d7 +color6 #70c0b1 +color14 #70c0b1 +color7 #fffefe +color15 #fffefe +selection_foreground #000000 diff --git a/kitty/kitty.conf b/kitty/kitty.conf new file mode 100644 index 0000000..7d5a7f9 --- /dev/null +++ b/kitty/kitty.conf @@ -0,0 +1,2297 @@ +# vim:fileencoding=utf-8:foldmethod=marker + +#: Fonts {{{ + +#: kitty has very powerful font management. You can configure +#: individual font faces and even specify special fonts for particular +#: characters. + +# font_family monospace +# bold_font auto +# italic_font auto +# bold_italic_font auto + +#: You can specify different fonts for the bold/italic/bold-italic +#: variants. To get a full list of supported fonts use the `kitty +#: +list-fonts` command. By default they are derived automatically, by +#: the OSes font system. When bold_font or bold_italic_font is set to +#: auto on macOS, the priority of bold fonts is semi-bold, bold, +#: heavy. Setting them manually is useful for font families that have +#: many weight variants like Book, Medium, Thick, etc. For example:: + +#: font_family Operator Mono Book +#: bold_font Operator Mono Medium +#: italic_font Operator Mono Book Italic +#: bold_italic_font Operator Mono Medium Italic + +# font_size 11.0 + +#: Font size (in pts) + +# force_ltr no + +#: kitty does not support BIDI (bidirectional text), however, for RTL +#: scripts, words are automatically displayed in RTL. That is to say, +#: in an RTL script, the words "HELLO WORLD" display in kitty as +#: "WORLD HELLO", and if you try to select a substring of an RTL- +#: shaped string, you will get the character that would be there had +#: the the string been LTR. For example, assuming the Hebrew word +#: ירושלים, selecting the character that on the screen appears to be ם +#: actually writes into the selection buffer the character י. kitty's +#: default behavior is useful in conjunction with a filter to reverse +#: the word order, however, if you wish to manipulate RTL glyphs, it +#: can be very challenging to work with, so this option is provided to +#: turn it off. Furthermore, this option can be used with the command +#: line program GNU FriBidi +#: to get BIDI +#: support, because it will force kitty to always treat the text as +#: LTR, which FriBidi expects for terminals. + +# symbol_map + +#: E.g. symbol_map U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols + +#: Map the specified Unicode codepoints to a particular font. Useful +#: if you need special rendering for some symbols, such as for +#: Powerline. Avoids the need for patched fonts. Each Unicode code +#: point is specified in the form `U+`. You +#: can specify multiple code points, separated by commas and ranges +#: separated by hyphens. This option can be specified multiple times. +#: The syntax is:: + +#: symbol_map codepoints Font Family Name + +# narrow_symbols + +#: E.g. narrow_symbols U+E0A0-U+E0A3,U+E0C0-U+E0C7 1 + +#: Usually, for Private Use Unicode characters and some symbol/dingbat +#: characters, if the character is followed by one or more spaces, +#: kitty will use those extra cells to render the character larger, if +#: the character in the font has a wide aspect ratio. Using this +#: option you can force kitty to restrict the specified code points to +#: render in the specified number of cells (defaulting to one cell). +#: This option can be specified multiple times. The syntax is:: + +#: narrow_symbols codepoints [optionally the number of cells] + +# disable_ligatures never + +#: Choose how you want to handle multi-character ligatures. The +#: default is to always render them. You can tell kitty to not render +#: them when the cursor is over them by using cursor to make editing +#: easier, or have kitty never render them at all by using always, if +#: you don't like them. The ligature strategy can be set per-window +#: either using the kitty remote control facility or by defining +#: shortcuts for it in kitty.conf, for example:: + +#: map alt+1 disable_ligatures_in active always +#: map alt+2 disable_ligatures_in all never +#: map alt+3 disable_ligatures_in tab cursor + +#: Note that this refers to programming ligatures, typically +#: implemented using the calt OpenType feature. For disabling general +#: ligatures, use the font_features option. + +# font_features + +#: E.g. font_features none + +#: Choose exactly which OpenType features to enable or disable. This +#: is useful as some fonts might have features worthwhile in a +#: terminal. For example, Fira Code includes a discretionary feature, +#: zero, which in that font changes the appearance of the zero (0), to +#: make it more easily distinguishable from Ø. Fira Code also includes +#: other discretionary features known as Stylistic Sets which have the +#: tags ss01 through ss20. + +#: For the exact syntax to use for individual features, see the +#: HarfBuzz documentation . + +#: Note that this code is indexed by PostScript name, and not the font +#: family. This allows you to define very precise feature settings; +#: e.g. you can disable a feature in the italic font but not in the +#: regular font. + +#: On Linux, font features are first read from the FontConfig database +#: and then this option is applied, so they can be configured in a +#: single, central place. + +#: To get the PostScript name for a font, use `kitty +list-fonts +#: --psnames`: + +#: .. code-block:: sh + +#: $ kitty +list-fonts --psnames | grep Fira +#: Fira Code +#: Fira Code Bold (FiraCode-Bold) +#: Fira Code Light (FiraCode-Light) +#: Fira Code Medium (FiraCode-Medium) +#: Fira Code Regular (FiraCode-Regular) +#: Fira Code Retina (FiraCode-Retina) + +#: The part in brackets is the PostScript name. + +#: Enable alternate zero and oldstyle numerals:: + +#: font_features FiraCode-Retina +zero +onum + +#: Enable only alternate zero in the bold font:: + +#: font_features FiraCode-Bold +zero + +#: Disable the normal ligatures, but keep the calt feature which (in +#: this font) breaks up monotony:: + +#: font_features TT2020StyleB-Regular -liga +calt + +#: In conjunction with force_ltr, you may want to disable Arabic +#: shaping entirely, and only look at their isolated forms if they +#: show up in a document. You can do this with e.g.:: + +#: font_features UnifontMedium +isol -medi -fina -init + +# modify_font + +#: Modify font characteristics such as the position or thickness of +#: the underline and strikethrough. The modifications can have the +#: suffix px for pixels or % for percentage of original value. No +#: suffix means use pts. For example:: + +#: modify_font underline_position -2 +#: modify_font underline_thickness 150% +#: modify_font strikethrough_position 2px + +#: Additionally, you can modify the size of the cell in which each +#: font glyph is rendered and the baseline at which the glyph is +#: placed in the cell. For example:: + +#: modify_font cell_width 80% +#: modify_font cell_height -2px +#: modify_font baseline 3 + +#: Note that modifying the baseline will automatically adjust the +#: underline and strikethrough positions by the same amount. +#: Increasing the baseline raises glyphs inside the cell and +#: decreasing it lowers them. Decreasing the cell size might cause +#: rendering artifacts, so use with care. + +# box_drawing_scale 0.001, 1, 1.5, 2 + +#: The sizes of the lines used for the box drawing Unicode characters. +#: These values are in pts. They will be scaled by the monitor DPI to +#: arrive at a pixel value. There must be four values corresponding to +#: thin, normal, thick, and very thick lines. + +# undercurl_style thin-sparse + +#: The style with which undercurls are rendered. This option takes the +#: form (thin|thick)-(sparse|dense). Thin and thick control the +#: thickness of the undercurl. Sparse and dense control how often the +#: curl oscillates. With sparse the curl will peak once per character, +#: with dense twice. + +#: }}} + +#: Cursor customization {{{ +# cursor #cccccc + +#: Default cursor color. If set to the special value none the cursor +#: will be rendered with a "reverse video" effect. It's color will be +#: the color of the text in the cell it is over and the text will be +#: rendered with the background color of the cell. Note that if the +#: program running in the terminal sets a cursor color, this takes +#: precedence. Also, the cursor colors are modified if the cell +#: background and foreground colors have very low contrast. +# cursor_text_color #111111 + +#: The color of text under the cursor. If you want it rendered with +#: the background color of the cell underneath instead, use the +#: special keyword: background. Note that if cursor is set to none +#: then this option is ignored. + +# cursor_shape block + +#: The cursor shape can be one of block, beam, underline. Note that +#: when reloading the config this will be changed only if the cursor +#: shape has not been set by the program running in the terminal. This +#: sets the default cursor shape, applications running in the terminal +#: can override it. In particular, shell integration +#: in kitty sets +#: the cursor shape to beam at shell prompts. You can avoid this by +#: setting shell_integration to no-cursor. + +# cursor_beam_thickness 1.5 + +#: The thickness of the beam cursor (in pts). + +# cursor_underline_thickness 2.0 + +#: The thickness of the underline cursor (in pts). + +# cursor_blink_interval -1 + +#: The interval to blink the cursor (in seconds). Set to zero to +#: disable blinking. Negative values mean use system default. Note +#: that the minimum interval will be limited to repaint_delay. + +# cursor_stop_blinking_after 15.0 + +#: Stop blinking cursor after the specified number of seconds of +#: keyboard inactivity. Set to zero to never stop blinking. + +#: }}} + +#: Scrollback {{{ + +# scrollback_lines 2000 + +#: Number of lines of history to keep in memory for scrolling back. +#: Memory is allocated on demand. Negative numbers are (effectively) +#: infinite scrollback. Note that using very large scrollback is not +#: recommended as it can slow down performance of the terminal and +#: also use large amounts of RAM. Instead, consider using +#: scrollback_pager_history_size. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER + +#: Program with which to view scrollback in a new window. The +#: scrollback buffer is passed as STDIN to this program. If you change +#: it, make sure the program you use can handle ANSI escape sequences +#: for colors and text formatting. INPUT_LINE_NUMBER in the command +#: line above will be replaced by an integer representing which line +#: should be at the top of the screen. Similarly CURSOR_LINE and +#: CURSOR_COLUMN will be replaced by the current cursor position or +#: set to 0 if there is no cursor, for example, when showing the last +#: command output. + +# scrollback_pager_history_size 0 + +#: Separate scrollback history size (in MB), used only for browsing +#: the scrollback buffer with pager. This separate buffer is not +#: available for interactive scrolling but will be piped to the pager +#: program when viewing scrollback buffer in a separate window. The +#: current implementation stores the data in UTF-8, so approximatively +#: 10000 lines per megabyte at 100 chars per line, for pure ASCII, +#: unformatted text. A value of zero or less disables this feature. +#: The maximum allowed size is 4GB. Note that on config reload if this +#: is changed it will only affect newly created windows, not existing +#: ones. + +# scrollback_fill_enlarged_window no + +#: Fill new space with lines from the scrollback buffer after +#: enlarging a window. + +# wheel_scroll_multiplier 5.0 + +#: Multiplier for the number of lines scrolled by the mouse wheel. +#: Note that this is only used for low precision scrolling devices, +#: not for high precision scrolling devices on platforms such as macOS +#: and Wayland. Use negative numbers to change scroll direction. See +#: also wheel_scroll_min_lines. + +# wheel_scroll_min_lines 1 + +#: The minimum number of lines scrolled by the mouse wheel. The scroll +#: multiplier wheel_scroll_multiplier only takes effect after it +#: reaches this number. Note that this is only used for low precision +#: scrolling devices like wheel mice that scroll by very small amounts +#: when using the wheel. With a negative number, the minimum number of +#: lines will always be added. + +# touch_scroll_multiplier 1.0 + +#: Multiplier for the number of lines scrolled by a touchpad. Note +#: that this is only used for high precision scrolling devices on +#: platforms such as macOS and Wayland. Use negative numbers to change +#: scroll direction. + +#: }}} + +#: Mouse {{{ + +# mouse_hide_wait 3.0 + +#: Hide mouse cursor after the specified number of seconds of the +#: mouse not being used. Set to zero to disable mouse cursor hiding. +#: Set to a negative value to hide the mouse cursor immediately when +#: typing text. Disabled by default on macOS as getting it to work +#: robustly with the ever-changing sea of bugs that is Cocoa is too +#: much effort. +# url_color #0087bd + url_style curly + +#: The color and style for highlighting URLs on mouse-over. url_style +#: can be one of: none, straight, double, curly, dotted, dashed. + + open_url_with default + +#: The program to open clicked URLs. The special value default with +#: first look for any URL handlers defined via the open_actions +#: facility and if non +#: are found, it will use the Operating System's default URL handler +#: (open on macOS and xdg-open on Linux). + +# url_prefixes file ftp ftps gemini git gopher http https irc ircs kitty mailto news sftp ssh + +#: The set of URL prefixes to look for when detecting a URL under the +#: mouse cursor. + + detect_urls yes + +#: Detect URLs under the mouse. Detected URLs are highlighted with an +#: underline and the mouse cursor becomes a hand over them. Even if +#: this option is disabled, URLs are still clickable. + +# url_excluded_characters + +#: Additional characters to be disallowed from URLs, when detecting +#: URLs under the mouse cursor. By default, all characters that are +#: legal in URLs are allowed. + +# show_hyperlink_targets no + +#: When the mouse hovers over a terminal hyperlink, show the actual +#: URL that will be activated when the hyperlink is clicked. + +# copy_on_select no + +#: Copy to clipboard or a private buffer on select. With this set to +#: clipboard, selecting text with the mouse will cause the text to be +#: copied to clipboard. Useful on platforms such as macOS that do not +#: have the concept of primary selection. You can instead specify a +#: name such as a1 to copy to a private kitty buffer. Map a shortcut +#: with the paste_from_buffer action to paste from this private +#: buffer. For example:: + +#: copy_on_select a1 + map shift+cmd+v paste_from_buffer a1 + +#: Note that copying to the clipboard is a security risk, as all +#: programs, including websites open in your browser can read the +#: contents of the system clipboard. + +# paste_actions quote-urls-at-prompt + +#: A comma separated list of actions to take when pasting text into +#: the terminal. The supported paste actions are: + +#: quote-urls-at-prompt: +#: If the text being pasted is a URL and the cursor is at a shell prompt, +#: automatically quote the URL (needs shell_integration). +#: confirm: +#: Confirm the paste if bracketed paste mode is not active or there is more +#: a large amount of text being pasted. +#: filter: +#: Run the filter_paste() function from the file paste-actions.py in +#: the kitty config directory on the pasted text. The text returned by the +#: function will be actually pasted. + +# strip_trailing_spaces never + +#: Remove spaces at the end of lines when copying to clipboard. A +#: value of smart will do it when using normal selections, but not +#: rectangle selections. A value of always will always do it. + +# select_by_word_characters @-./_~?&=%+# + +#: Characters considered part of a word when double clicking. In +#: addition to these characters any character that is marked as an +#: alphanumeric character in the Unicode database will be matched. + +# select_by_word_characters_forward + +#: Characters considered part of a word when extending the selection +#: forward on double clicking. In addition to these characters any +#: character that is marked as an alphanumeric character in the +#: Unicode database will be matched. + +#: If empty (default) select_by_word_characters will be used for both +#: directions. + +# click_interval -1.0 + +#: The interval between successive clicks to detect double/triple +#: clicks (in seconds). Negative numbers will use the system default +#: instead, if available, or fallback to 0.5. + +# focus_follows_mouse no + +#: Set the active window to the window under the mouse when moving the +#: mouse around. + +# pointer_shape_when_grabbed arrow + +#: The shape of the mouse pointer when the program running in the +#: terminal grabs the mouse. Valid values are: arrow, beam and hand. + +# default_pointer_shape beam + +#: The default shape of the mouse pointer. Valid values are: arrow, +#: beam and hand. + +# pointer_shape_when_dragging beam + +#: The default shape of the mouse pointer when dragging across text. +#: Valid values are: arrow, beam and hand. + +#: Mouse actions {{{ + +#: Mouse buttons can be mapped to perform arbitrary actions. The +#: syntax is: + +#: .. code-block:: none + +#: mouse_map button-name event-type modes action + +#: Where button-name is one of left, middle, right, b1 ... b8 with +#: added keyboard modifiers. For example: ctrl+shift+left refers to +#: holding the Ctrl+Shift keys while clicking with the left mouse +#: button. The value b1 ... b8 can be used to refer to up to eight +#: buttons on a mouse. + +#: event-type is one of press, release, doublepress, triplepress, +#: click, doubleclick. modes indicates whether the action is performed +#: when the mouse is grabbed by the program running in the terminal, +#: or not. The values are grabbed or ungrabbed or a comma separated +#: combination of them. grabbed refers to when the program running in +#: the terminal has requested mouse events. Note that the click and +#: double click events have a delay of click_interval to disambiguate +#: from double and triple presses. + +#: You can run kitty with the kitty --debug-input command line option +#: to see mouse events. See the builtin actions below to get a sense +#: of what is possible. + +#: If you want to unmap an action, map it to no_op. For example, to +#: disable opening of URLs with a plain click:: + +#: mouse_map left click ungrabbed no_op + +#: See all the mappable actions including mouse actions here +#: . + +#: .. note:: +#: Once a selection is started, releasing the button that started it will +#: automatically end it and no release event will be dispatched. + +# clear_all_mouse_actions no + +#: Remove all mouse action definitions up to this point. Useful, for +#: instance, to remove the default mouse actions. + +#: Click the link under the mouse or move the cursor + +# mouse_map left click ungrabbed mouse_handle_click selection link prompt + +#:: First check for a selection and if one exists do nothing. Then +#:: check for a link under the mouse cursor and if one exists, click +#:: it. Finally check if the click happened at the current shell +#:: prompt and if so, move the cursor to the click location. Note +#:: that this requires shell integration +#:: to work. + +#: Click the link under the mouse or move the cursor even when grabbed + +# mouse_map shift+left click grabbed,ungrabbed mouse_handle_click selection link prompt + +#:: Same as above, except that the action is performed even when the +#:: mouse is grabbed by the program running in the terminal. + +#: Click the link under the mouse cursor + +# mouse_map ctrl+shift+left release grabbed,ungrabbed mouse_handle_click link + +#:: Variant with Ctrl+Shift is present because the simple click based +#:: version has an unavoidable delay of click_interval, to +#:: disambiguate clicks from double clicks. + +#: Discard press event for link click + +# mouse_map ctrl+shift+left press grabbed discard_event + +#:: Prevent this press event from being sent to the program that has +#:: grabbed the mouse, as the corresponding release event is used to +#:: open a URL. + +#: Paste from the primary selection + +# mouse_map middle release ungrabbed paste_from_selection + +#: Start selecting text + +# mouse_map left press ungrabbed mouse_selection normal + +#: Start selecting text in a rectangle + +# mouse_map ctrl+alt+left press ungrabbed mouse_selection rectangle + +#: Select a word + +# mouse_map left doublepress ungrabbed mouse_selection word + +#: Select a line + +# mouse_map left triplepress ungrabbed mouse_selection line + +#: Select line from point + +# mouse_map ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line. + +#: Extend the current selection + +# mouse_map right press ungrabbed mouse_selection extend + +#:: If you want only the end of the selection to be moved instead of +#:: the nearest boundary, use move-end instead of extend. + +#: Paste from the primary selection even when grabbed + +# mouse_map shift+middle release ungrabbed,grabbed paste_selection +# mouse_map shift+middle press grabbed discard_event + +#: Start selecting text even when grabbed + +# mouse_map shift+left press ungrabbed,grabbed mouse_selection normal + +#: Start selecting text in a rectangle even when grabbed + +# mouse_map ctrl+shift+alt+left press ungrabbed,grabbed mouse_selection rectangle + +#: Select a word even when grabbed + +# mouse_map shift+left doublepress ungrabbed,grabbed mouse_selection word + +#: Select a line even when grabbed + +# mouse_map shift+left triplepress ungrabbed,grabbed mouse_selection line + +#: Select line from point even when grabbed + +# mouse_map ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point + +#:: Select from the clicked point to the end of the line even when +#:: grabbed. + +#: Extend the current selection even when grabbed + +# mouse_map shift+right press ungrabbed,grabbed mouse_selection extend + +#: Show clicked command output in pager + +# mouse_map ctrl+shift+right press ungrabbed mouse_show_command_output + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: }}} + +#: Performance tuning {{{ + +# repaint_delay 10 + +#: Delay between screen updates (in milliseconds). Decreasing it, +#: increases frames-per-second (FPS) at the cost of more CPU usage. +#: The default value yields ~100 FPS which is more than sufficient for +#: most uses. Note that to actually achieve 100 FPS, you have to +#: either set sync_to_monitor to no or use a monitor with a high +#: refresh rate. Also, to minimize latency when there is pending input +#: to be processed, this option is ignored. + +# input_delay 3 + +#: Delay before input from the program running in the terminal is +#: processed (in milliseconds). Note that decreasing it will increase +#: responsiveness, but also increase CPU usage and might cause flicker +#: in full screen programs that redraw the entire screen on each loop, +#: because kitty is so fast that partial screen updates will be drawn. + +# sync_to_monitor yes + +#: Sync screen updates to the refresh rate of the monitor. This +#: prevents screen tearing +#: when scrolling. +#: However, it limits the rendering speed to the refresh rate of your +#: monitor. With a very high speed mouse/high keyboard repeat rate, +#: you may notice some slight input latency. If so, set this to no. + +#: }}} + +#: Terminal bell {{{ + +# enable_audio_bell yes + +#: The audio bell. Useful to disable it in environments that require +#: silence. + +# visual_bell_duration 0.0 + +#: The visual bell duration (in seconds). Flash the screen when a bell +#: occurs for the specified number of seconds. Set to zero to disable. + +# visual_bell_color none + +#: The color used by visual bell. Set to none will fall back to +#: selection background color. If you feel that the visual bell is too +#: bright, you can set it to a darker color. + +# window_alert_on_bell yes + +#: Request window attention on bell. Makes the dock icon bounce on +#: macOS or the taskbar flash on linux. + +# bell_on_tab "🔔 " + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has a bell. If you want to use leading +#: or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +#: For backwards compatibility, values of yes, y and true are +#: converted to the default bell symbol and no, n, false and none are +#: converted to the empty string. + +# command_on_bell none + +#: Program to run when a bell occurs. The environment variable +#: KITTY_CHILD_CMDLINE can be used to get the program running in the +#: window in which the bell occurred. + +# bell_path none + +#: Path to a sound file to play as the bell sound. If set to none, the +#: system default bell sound is used. Must be in a format supported by +#: the operating systems sound API, such as WAV or OGA on Linux +#: (libcanberra) or AIFF, MP3 or WAV on macOS (NSSound) + +#: }}} + +#: Window layout {{{ + + remember_window_size yes + initial_window_width 640 + initial_window_height 400 + +#: If enabled, the OS Window size will be remembered so that new +#: instances of kitty will have the same size as the previous +#: instance. If disabled, the OS Window will initially have size +#: configured by initial_window_width/height, in pixels. You can use a +#: suffix of "c" on the width/height values to have them interpreted +#: as number of cells instead of pixels. + + enabled_layouts * + +#: The enabled window layouts. A comma separated list of layout names. +#: The special value all means all layouts. The first listed layout +#: will be used as the startup layout. Default configuration is all +#: layouts in alphabetical order. For a list of available layouts, see +#: the layouts . + +# window_resize_step_cells 2 +# window_resize_step_lines 2 + +#: The step size (in units of cell width/cell height) to use when +#: resizing kitty windows in a layout with the shortcut +#: start_resizing_window. The cells value is used for horizontal +#: resizing, and the lines value is used for vertical resizing. + +# window_border_width 0.5pt + +#: The width of window borders. Can be either in pixels (px) or pts +#: (pt). Values in pts will be rounded to the nearest number of pixels +#: based on screen resolution. If not specified, the unit is assumed +#: to be pts. Note that borders are displayed only when more than one +#: window is visible. They are meant to separate multiple windows. + + draw_minimal_borders yes + +#: Draw only the minimum borders needed. This means that only the +#: borders that separate the window from a neighbor are drawn. Note +#: that setting a non-zero window_margin_width overrides this and +#: causes all borders to be drawn. + +# window_margin_width 0 + +#: The window margin (in pts) (blank area outside the border). A +#: single value sets all four sides. Two values set the vertical and +#: horizontal sides. Three values set top, horizontal and bottom. Four +#: values set top, right, bottom and left. + +# single_window_margin_width -1 + +#: The window margin to use when only a single window is visible (in +#: pts). Negative values will cause the value of window_margin_width +#: to be used instead. A single value sets all four sides. Two values +#: set the vertical and horizontal sides. Three values set top, +#: horizontal and bottom. Four values set top, right, bottom and left. + +# window_padding_width 0 + +#: The window padding (in pts) (blank area between the text and the +#: window border). A single value sets all four sides. Two values set +#: the vertical and horizontal sides. Three values set top, horizontal +#: and bottom. Four values set top, right, bottom and left. + +# placement_strategy center + +#: When the window size is not an exact multiple of the cell size, the +#: cell area of the terminal window will have some extra padding on +#: the sides. You can control how that padding is distributed with +#: this option. Using a value of center means the cell area will be +#: placed centrally. A value of top-left means the padding will be +#: only at the bottom and right edges. +# active_border_color #00ff00 + +#: The color for the border of the active window. Set this to none to +#: not draw borders around the active window. +# inactive_border_color #cccccc + +#: The color for the border of inactive windows. + +# bell_border_color #ff5a00 + +#: The color for the border of inactive windows in which a bell has +#: occurred. + +# inactive_text_alpha 1.0 + +#: Fade the text in inactive windows by the specified amount (a number +#: between zero and one, with zero being fully faded). + +# hide_window_decorations no + +#: Hide the window decorations (title-bar and window borders) with +#: yes. On macOS, titlebar-only can be used to only hide the titlebar. +#: Whether this works and exactly what effect it has depends on the +#: window manager/operating system. Note that the effects of changing +#: this option when reloading config are undefined. + +# window_logo_path none + +#: Path to a logo image. Must be in PNG format. Relative paths are +#: interpreted relative to the kitty config directory. The logo is +#: displayed in a corner of every kitty window. The position is +#: controlled by window_logo_position. Individual windows can be +#: configured to have different logos either using the launch action +#: or the remote control facility. + +# window_logo_position bottom-right + +#: Where to position the window logo in the window. The value can be +#: one of: top-left, top, top-right, left, center, right, bottom-left, +#: bottom, bottom-right. + +# window_logo_alpha 0.5 + +#: The amount the logo should be faded into the background. With zero +#: being fully faded and one being fully opaque. + +# resize_debounce_time 0.1 + +#: The time to wait before redrawing the screen when a resize event is +#: received (in seconds). On platforms such as macOS, where the +#: operating system sends events corresponding to the start and end of +#: a resize, this number is ignored. + +# resize_draw_strategy static + +#: Choose how kitty draws a window while a resize is in progress. A +#: value of static means draw the current window contents, mostly +#: unchanged. A value of scale means draw the current window contents +#: scaled. A value of blank means draw a blank window. A value of size +#: means show the window size in cells. + +# resize_in_steps no + +#: Resize the OS window in steps as large as the cells, instead of +#: with the usual pixel accuracy. Combined with initial_window_width +#: and initial_window_height in number of cells, this option can be +#: used to keep the margins as small as possible when resizing the OS +#: window. Note that this does not currently work on Wayland. + +# visual_window_select_characters 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ + +#: The list of characters for visual window selection. For example, +#: for selecting a window to focus on with focus_visible_window. The +#: value should be a series of unique numbers or alphabets, case +#: insensitive, from the set [0-9A-Z]. Specify your preference as a +#: string of characters. + +# confirm_os_window_close -1 + +#: Ask for confirmation when closing an OS window or a tab with at +#: least this number of kitty windows in it by window manager (e.g. +#: clicking the window close button or pressing the operating system +#: shortcut to close windows) or by the close_tab action. A value of +#: zero disables confirmation. This confirmation also applies to +#: requests to quit the entire application (all OS windows, via the +#: quit action). Negative values are converted to positive ones, +#: however, with shell_integration enabled, using negative values +#: means windows sitting at a shell prompt are not counted, only +#: windows where some command is currently running. Note that if you +#: want confirmation when closing individual windows, you can map the +#: close_window_with_confirmation action. + +#: }}} + +#: Tab bar {{{ + +# tab_bar_edge bottom + +#: The edge to show the tab bar on, top or bottom. + +# tab_bar_margin_width 0.0 + +#: The margin to the left and right of the tab bar (in pts). + +# tab_bar_margin_height 0.0 0.0 + +#: The margin above and below the tab bar (in pts). The first number +#: is the margin between the edge of the OS Window and the tab bar. +#: The second number is the margin between the tab bar and the +#: contents of the current tab. + +# tab_bar_style fade + +#: The tab bar style, can be one of: + +#: fade +#: Each tab's edges fade into the background color. (See also tab_fade) +#: slant +#: Tabs look like the tabs in a physical file. +#: separator +#: Tabs are separated by a configurable separator. (See also +#: tab_separator) +#: powerline +#: Tabs are shown as a continuous line with "fancy" separators. +#: (See also tab_powerline_style) +#: custom +#: A user-supplied Python function called draw_tab is loaded from the file +#: tab_bar.py in the kitty config directory. For examples of how to +#: write such a function, see the functions named draw_tab_with_* in +#: kitty's source code: kitty/tab_bar.py. See also +#: this discussion +#: for examples from kitty users. +#: hidden +#: The tab bar is hidden. If you use this, you might want to create a mapping +#: for the select_tab action which presents you with a list of tabs and +#: allows for easy switching to a tab. + +# tab_bar_align left + +#: The horizontal alignment of the tab bar, can be one of: left, +#: center, right. + +# tab_bar_min_tabs 2 + +#: The minimum number of tabs that must exist before the tab bar is +#: shown. + +# tab_switch_strategy previous + +#: The algorithm to use when switching to a tab when the current tab +#: is closed. The default of previous will switch to the last used +#: tab. A value of left will switch to the tab to the left of the +#: closed tab. A value of right will switch to the tab to the right of +#: the closed tab. A value of last will switch to the right-most tab. + +# tab_fade 0.25 0.5 0.75 1 + +#: Control how each tab fades into the background when using fade for +#: the tab_bar_style. Each number is an alpha (between zero and one) +#: that controls how much the corresponding cell fades into the +#: background, with zero being no fade and one being full fade. You +#: can change the number of cells used by adding/removing entries to +#: this list. + +# tab_separator " ┇" + +#: The separator between tabs in the tab bar when using separator as +#: the tab_bar_style. + +# tab_powerline_style angled + +#: The powerline separator style between tabs in the tab bar when +#: using powerline as the tab_bar_style, can be one of: angled, +#: slanted, round. + +# tab_activity_symbol none + +#: Some text or a Unicode symbol to show on the tab if a window in the +#: tab that does not have focus has some activity. If you want to use +#: leading or trailing spaces, surround the text with quotes. See +#: tab_title_template for how this is rendered. + +# tab_title_max_length 0 + +#: The maximum number of cells that can be used to render the text in +#: a tab. A value of zero means that no limit is applied. + +# tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}" + +#: A template to render the tab title. The default just renders the +#: title with optional symbols for bell and activity. If you wish to +#: include the tab-index as well, use something like: {index}:{title}. +#: Useful if you have shortcuts mapped for goto_tab N. If you prefer +#: to see the index as a superscript, use {sup.index}. All data +#: available is: + +#: title +#: The current tab title. +#: index +#: The tab index useable with goto_tab N goto_tab shortcuts. +#: layout_name +#: The current layout name. +#: num_windows +#: The number of windows in the tab. +#: num_window_groups +#: The number of window groups (not counting overlay windows) in the tab. +#: tab.active_wd +#: The working directory of the currently active window in the tab (expensive, +#: requires syscall). Use active_oldest_wd to get the directory of the oldest foreground process rather than the newest. +#: tab.active_exe +#: The name of the executable running in the foreground of the currently active window in the tab (expensive, +#: requires syscall). Use active_oldest_exe for the oldest foreground process. +#: max_title_length +#: The maximum title length available. + +#: Note that formatting is done by Python's string formatting +#: machinery, so you can use, for instance, {layout_name[:2].upper()} +#: to show only the first two letters of the layout name, upper-cased. +#: If you want to style the text, you can use styling directives, for +#: example: +#: `{fmt.fg.red}red{fmt.fg.tab}normal{fmt.bg._00FF00}greenbg{fmt.bg.tab}`. +#: Similarly, for bold and italic: +#: `{fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}`. +#: Note that for backward compatibility, if {bell_symbol} or +#: {activity_symbol} are not present in the template, they are +#: prepended to it. + +# active_tab_title_template none + +#: Template to use for active tabs. If not specified falls back to +#: tab_title_template. + +# active_tab_foreground #000 +# active_tab_background #eee +# active_tab_font_style bold-italic +# inactive_tab_foreground #444 +# inactive_tab_background #999 +# inactive_tab_font_style normal + +#: Tab bar colors and styles. + +# tab_bar_background none + +#: Background color for the tab bar. Defaults to using the terminal +#: background color. + +# tab_bar_margin_color none + +#: Color for the tab bar margin area. Defaults to using the terminal +#: background color for margins above and below the tab bar. For side +#: margins the default color is chosen to match the background color +#: of the neighboring tab. + +#: }}} + +#: Color scheme {{{ + +# foreground #dddddd +# background #000000 + +#: The foreground and background colors. + +# background_opacity 1.0 + +#: The opacity of the background. A number between zero and one, where +#: one is opaque and zero is fully transparent. This will only work if +#: supported by the OS (for instance, when using a compositor under +#: X11). Note that it only sets the background color's opacity in +#: cells that have the same background color as the default terminal +#: background, so that things like the status bar in vim, powerline +#: prompts, etc. still look good. But it means that if you use a color +#: theme with a background color in your editor, it will not be +#: rendered as transparent. Instead you should change the default +#: background color in your kitty config and not use a background +#: color in the editor color scheme. Or use the escape codes to set +#: the terminals default colors in a shell script to launch your +#: editor. Be aware that using a value less than 1.0 is a (possibly +#: significant) performance hit. If you want to dynamically change +#: transparency of windows, set dynamic_background_opacity to yes +#: (this is off by default as it has a performance cost). Changing +#: this option when reloading the config will only work if +#: dynamic_background_opacity was enabled in the original config. + +# background_image none + +#: Path to a background image. Must be in PNG format. + +# background_image_layout tiled + +#: Whether to tile, scale or clamp the background image. The value can +#: be one of tiled, mirror-tiled, scaled, clamped or centered. + +# background_image_linear no + +#: When background image is scaled, whether linear interpolation +#: should be used. + +# dynamic_background_opacity no + +#: Allow changing of the background_opacity dynamically, using either +#: keyboard shortcuts (increase_background_opacity and +#: decrease_background_opacity) or the remote control facility. +#: Changing this option by reloading the config is not supported. + +# background_tint 0.0 + +#: How much to tint the background image by the background color. This +#: option makes it easier to read the text. Tinting is done using the +#: current background color for each window. This option applies only +#: if background_opacity is set and transparent windows are supported +#: or background_image is set. + +# background_tint_gaps 1.0 + +#: How much to tint the background image at the window gaps by the +#: background color, after applying background_tint. Since this is +#: multiplicative with background_tint, it can be used to lighten the +#: tint over the window gaps for a *separated* look. + +# dim_opacity 0.75 + +#: How much to dim text that has the DIM/FAINT attribute set. One +#: means no dimming and zero means fully dimmed (i.e. invisible). + +# selection_foreground #000000 +# selection_background #fffacd + +#: The foreground and background colors for text selected with the +#: mouse. Setting both of these to none will cause a "reverse video" +#: effect for selections, where the selection will be the cell text +#: color and the text will become the cell background color. Setting +#: only selection_foreground to none will cause the foreground color +#: to be used unchanged. Note that these colors can be overridden by +#: the program running in the terminal. + +#: The color table {{{ + +#: The 256 terminal colors. There are 8 basic colors, each color has a +#: dull and bright version, for the first 16 colors. You can set the +#: remaining 240 colors as color16 to color255. + +# color0 #000000 +# color8 #767676 + +#: black + +# color1 #cc0403 +# color9 #f2201f + +#: red + +# color2 #19cb00 +# color10 #23fd00 + +#: green + +# color3 #cecb00 +# color11 #fffd00 + +#: yellow + +# color4 #0d73cc +# color12 #1a8fff + +#: blue + +# color5 #cb1ed1 +# color13 #fd28ff + +#: magenta + +# color6 #0dcdcd +# color14 #14ffff + +#: cyan + +# color7 #dddddd +# color15 #ffffff + +#: white + +# mark1_foreground black + +#: Color for marks of type 1 + +# mark1_background #98d3cb + +#: Color for marks of type 1 (light steel blue) + +# mark2_foreground black + +#: Color for marks of type 2 + +# mark2_background #f2dcd3 + +#: Color for marks of type 1 (beige) + +# mark3_foreground black + +#: Color for marks of type 3 + +# mark3_background #f274bc + +#: Color for marks of type 3 (violet) + +#: }}} + +#: }}} + +#: Advanced {{{ + +# shell . + +#: The shell program to execute. The default value of . means to use +#: whatever shell is set as the default shell for the current user. +#: Note that on macOS if you change this, you might need to add +#: --login and --interactive to ensure that the shell starts in +#: interactive mode and reads its startup rc files. + +# editor . + +#: The terminal based text editor (such as vim or nano) to use when +#: editing the kitty config file or similar tasks. + +#: The default value of . means to use the environment variables +#: VISUAL and EDITOR in that order. If these variables aren't set, +#: kitty will run your shell ($SHELL -l -i -c env) to see if your +#: shell startup rc files set VISUAL or EDITOR. If that doesn't work, +#: kitty will cycle through various known editors (vim, emacs, etc.) +#: and take the first one that exists on your system. + +# close_on_child_death no + +#: Close the window when the child process (shell) exits. With the +#: default value no, the terminal will remain open when the child +#: exits as long as there are still processes outputting to the +#: terminal (for example disowned or backgrounded processes). When +#: enabled with yes, the window will close as soon as the child +#: process exits. Note that setting it to yes means that any +#: background processes still using the terminal can fail silently +#: because their stdout/stderr/stdin no longer work. + +# remote_control_password + +#: Allow other programs to control kitty using passwords. This option +#: can be specified multiple times tpicko add multiple passwords. If no +#: passwords are present kitty will ask the user for permission if a +#: program tries to use remote control with a password. A password can +#: also *optionally* be associated with a set of allowed remote +#: control actions. For example:: + +#: remote_control_password "my passphrase" get-colors set-colors focus-window focus-tab + +#: Only the specified actions will be allowed when using this +#: password. Glob patterns can be used too, for example:: + +#: remote_control_password "my passphrase" set-tab-* resize-* + +#: To get a list of available actions, run:: + +#: kitty @ --help + +#: A set of actions to be allowed when no password is sent can be +#: specified by using an empty password, for example:: + +#: remote_control_password "" *-colors + +#: Finally, the path to a python module can be specified that provides +#: a function is_cmd_allowed that is used to check every remote +#: control command. See rc_custom_auth +#: +#: for details. For example:: + +#: remote_control_password "my passphrase" my_rc_command_checker.py + +#: Relative paths are resolved from the kitty configuration directory. + +# allow_remote_control no + +#: Allow other programs to control kitty. If you turn this on, other +#: programs can control all aspects of kitty, including sending text +#: to kitty windows, opening new windows, closing windows, reading the +#: content of windows, etc. Note that this even works over SSH +#: connections. The default setting of no prevents any form of remote +#: control. The meaning of the various values are: + +#: password +#: Remote control requests received over both the TTY device and the socket are +#: confirmed based on passwords, see remote_control_password. + +#: socket-only +#: Remote control requests received over a socket are accepted unconditionally. +#: Requests received over the TTY are denied. See listen_on. + +#: socket +#: Remote control requests received over a socket are accepted unconditionally. +#: Requests received over the TTY are confirmed based on password. + +#: no +#: Remote control is completely disabled. + +#: yes +#: Remote control requests are always accepted. + +# listen_on none + +#: Listen to the specified UNIX socket for remote control connections. +#: Note that this will apply to all kitty instances. It can be +#: overridden by the kitty --listen-on command line option, which also +#: supports listening on a TCP socket. This option accepts only UNIX +#: sockets, such as unix:${TEMP}/mykitty or unix:@mykitty (on Linux). +#: Environment variables are expanded and relative paths are resolved +#: with respect to the temporary directory. If {kitty_pid} is present, +#: then it is replaced by the PID of the kitty process, otherwise the +#: PID of the kitty process is appended to the value, with a hyphen. +#: See the help for kitty --listen-on for more details. Note that this +#: will be ignored unless allow_remote_control is set to either: yes, +#: socket or socket-only. Changing this option by reloading the config +#: is not supported. + +# env + +#: Specify the environment variables to be set in all child processes. +#: Using the name with an equal sign (e.g. env VAR=) will set it to +#: the empty string. Specifying only the name (e.g. env VAR) will +#: remove the variable from the child process' environment. Note that +#: environment variables are expanded recursively, for example:: + +#: env VAR1=a +#: env VAR2=${HOME}/${VAR1}/b + +#: The value of VAR2 will be /a/b. + +# watcher + +#: Path to python file which will be loaded for watchers +#: . Can be +#: specified more than once to load multiple watchers. The watchers +#: will be added to every kitty window. Relative paths are resolved +#: relative to the kitty config directory. Note that reloading the +#: config will only affect windows created after the reload. + +# exe_search_path + +#: Control where kitty finds the programs to run. The default search +#: order is: First search the system wide PATH, then ~/.local/bin and +#: ~/bin. If still not found, the PATH defined in the login shell +#: after sourcing all its startup files is tried. Finally, if present, +#: the PATH specified by the env option is tried. + +#: This option allows you to prepend, append, or remove paths from +#: this search order. It can be specified multiple times for multiple +#: paths. A simple path will be prepended to the search order. A path +#: that starts with the + sign will be append to the search order, +#: after ~/bin above. A path that starts with the - sign will be +#: removed from the entire search order. For example:: + +#: exe_search_path /some/prepended/path +#: exe_search_path +/some/appended/path +#: exe_search_path -/some/excluded/path + +# update_check_interval 24 + +#: The interval to periodically check if an update to kitty is +#: available (in hours). If an update is found, a system notification +#: is displayed informing you of the available update. The default is +#: to check every 24 hours, set to zero to disable. Update checking is +#: only done by the official binary builds. Distro packages or source +#: builds do not do update checking. Changing this option by reloading +#: the config is not supported. + +# startup_session none + +#: Path to a session file to use for all kitty instances. Can be +#: overridden by using the kitty --session command line option for +#: individual instances. See sessions +#: in the kitty +#: documentation for details. Note that relative paths are interpreted +#: with respect to the kitty config directory. Environment variables +#: in the path are expanded. Changing this option by reloading the +#: config is not supported. + +# clipboard_control write-clipboard write-primary read-clipboard-ask read-primary-ask + +#: Allow programs running in kitty to read and write from the +#: clipboard. You can control exactly which actions are allowed. The +#: possible actions are: write-clipboard, read-clipboard, write- +#: primary, read-primary, read-clipboard-ask, read-primary-ask. The +#: default is to allow writing to the clipboard and primary selection +#: and to ask for permission when a program tries to read from the +#: clipboard. Note that disabling the read confirmation is a security +#: risk as it means that any program, even the ones running on a +#: remote server via SSH can read your clipboard. See also +#: clipboard_max_size. + +# clipboard_max_size 512 + +#: The maximum size (in MB) of data from programs running in kitty +#: that will be stored for writing to the system clipboard. A value of +#: zero means no size limit is applied. See also clipboard_control. + +# file_transfer_confirmation_bypass + +#: The password that can be supplied to the file transfer kitten +#: to skip the +#: transfer confirmation prompt. This should only be used when +#: initiating transfers from trusted computers, over trusted networks +#: or encrypted transports, as it allows any programs running on the +#: remote machine to read/write to the local filesystem, without +#: permission. + +# allow_hyperlinks yes + +#: Process hyperlink escape sequences (OSC 8). If disabled OSC 8 +#: escape sequences are ignored. Otherwise they become clickable +#: links, that you can click with the mouse or by using the hints +#: kitten . The +#: special value of ask means that kitty will ask before opening the +#: link when clicked. + +# shell_integration enabled + +#: Enable shell integration on supported shells. This enables features +#: such as jumping to previous prompts, browsing the output of the +#: previous command in a pager, etc. on supported shells. Set to +#: disabled to turn off shell integration, completely. It is also +#: possible to disable individual features, set to a space separated +#: list of these values: no-rc, no-cursor, no-title, no-cwd, no- +#: prompt-mark, no-complete. See Shell integration +#: for details. + +# allow_cloning ask + +#: Control whether programs running in the terminal can request new +#: windows to be created. The canonical example is clone-in-kitty +#: . +#: By default, kitty will ask for permission for each clone request. +#: Allowing cloning unconditionally gives programs running in the +#: terminal (including over SSH) permission to execute arbitrary code, +#: as the user who is running the terminal, on the computer that the +#: terminal is running on. + +# clone_source_strategies venv,conda,env_var,path + +#: Control what shell code is sourced when running clone-in-kitty in +#: the newly cloned window. The supported strategies are: + +#: venv +#: Source the file $VIRTUAL_ENV/bin/activate. This is used by the +#: Python stdlib venv module and allows cloning venvs automatically. +#: conda +#: Run conda activate $CONDA_DEFAULT_ENV. This supports the virtual +#: environments created by conda. +#: env_var +#: Execute the contents of the environment variable +#: KITTY_CLONE_SOURCE_CODE with eval. +#: path +#: Source the file pointed to by the environment variable +#: KITTY_CLONE_SOURCE_PATH. + +#: This option must be a comma separated list of the above values. +#: This only source the first valid one in the above order. + +# term xterm-kitty + +#: The value of the TERM environment variable to set. Changing this +#: can break many terminal programs, only change it if you know what +#: you are doing, not because you read some advice on "Stack Overflow" +#: to change it. The TERM variable is used by various programs to get +#: information about the capabilities and behavior of the terminal. If +#: you change it, depending on what programs you run, and how +#: different the terminal you are changing it to is, various things +#: from key-presses, to colors, to various advanced features may not +#: work. Changing this option by reloading the config will only affect +#: newly created windows. + +#: }}} + +#: OS specific tweaks {{{ + +# wayland_titlebar_color system + +#: The color of the kitty window's titlebar on Wayland systems with +#: client side window decorations such as GNOME. A value of system +#: means to use the default system color, a value of background means +#: to use the background color of the currently active window and +#: finally you can use an arbitrary color, such as #12af59 or red. + +# macos_titlebar_color system + +#: The color of the kitty window's titlebar on macOS. A value of +#: system means to use the default system color, light or dark can +#: also be used to set it explicitly. A value of background means to +#: use the background color of the currently active window and finally +#: you can use an arbitrary color, such as #12af59 or red. WARNING: +#: This option works by using a hack when arbitrary color (or +#: background) is configured, as there is no proper Cocoa API for it. +#: It sets the background color of the entire window and makes the +#: titlebar transparent. As such it is incompatible with +#: background_opacity. If you want to use both, you are probably +#: better off just hiding the titlebar with hide_window_decorations. + +# macos_option_as_alt no + +#: Use the Option key as an Alt key on macOS. With this set to no, +#: kitty will use the macOS native Option+Key to enter Unicode +#: character behavior. This will break any Alt+Key keyboard shortcuts +#: in your terminal programs, but you can use the macOS Unicode input +#: technique. You can use the values: left, right or both to use only +#: the left, right or both Option keys as Alt, instead. Note that +#: kitty itself always treats Option the same as Alt. This means you +#: cannot use this option to configure different kitty shortcuts for +#: Option+Key vs. Alt+Key. Also, any kitty shortcuts using +#: Option/Alt+Key will take priority, so that any such key presses +#: will not be passed to terminal programs running inside kitty. +#: Changing this option by reloading the config is not supported. + +# macos_hide_from_tasks no + +#: Hide the kitty window from running tasks on macOS (⌘+Tab and the +#: Dock). Changing this option by reloading the config is not +#: supported. + +# macos_quit_when_last_window_closed no + +#: Have kitty quit when all the top-level windows are closed on macOS. +#: By default, kitty will stay running, even with no open windows, as +#: is the expected behavior on macOS. + +# macos_window_resizable yes + +#: Disable this if you want kitty top-level OS windows to not be +#: resizable on macOS. Changing this option by reloading the config +#: will only affect newly created OS windows. + +# macos_thicken_font 0 + +#: Draw an extra border around the font with the given width, to +#: increase legibility at small font sizes on macOS. For example, a +#: value of 0.75 will result in rendering that looks similar to sub- +#: pixel antialiasing at common font sizes. + +# macos_traditional_fullscreen no + +#: Use the macOS traditional full-screen transition, that is faster, +#: but less pretty. + +# macos_show_window_title_in all + +#: Control where the window title is displayed on macOS. A value of +#: window will show the title of the currently active window at the +#: top of the macOS window. A value of menubar will show the title of +#: the currently active window in the macOS global menu bar, making +#: use of otherwise wasted space. A value of all will show the title +#: in both places, and none hides the title. See +#: macos_menubar_title_max_length for how to control the length of the +#: title in the menu bar. + +# macos_menubar_title_max_length 0 + +#: The maximum number of characters from the window title to show in +#: the macOS global menu bar. Values less than one means that there is +#: no maximum limit. + +# macos_custom_beam_cursor no + +#: Use a custom mouse cursor for macOS that is easier to see on both +#: light and dark backgrounds. Nowadays, the default macOS cursor +#: already comes with a white border. WARNING: this might make your +#: mouse cursor invisible on dual GPU machines. Changing this option +#: by reloading the config is not supported. + +# macos_colorspace srgb + +#: The colorspace in which to interpret terminal colors. The default +#: of srgb will cause colors to match those seen in web browsers. The +#: value of default will use whatever the native colorspace of the +#: display is. The value of displayp3 will use Apple's special +#: snowflake display P3 color space, which will result in over +#: saturated (brighter) colors with some color shift. Reloading +#: configuration will change this value only for newly created OS +#: windows. + +# linux_display_server auto + +#: Choose between Wayland and X11 backends. By default, an appropriate +#: backend based on the system state is chosen automatically. Set it +#: to x11 or wayland to force the choice. Changing this option by +#: reloading the config is not supported. + +#: }}} + +#: Keyboard shortcuts {{{ + +#: Keys are identified simply by their lowercase Unicode characters. +#: For example: a for the A key, [ for the left square bracket key, +#: etc. For functional keys, such as Enter or Escape, the names are +#: present at Functional key definitions +#: . +#: For modifier keys, the names are ctrl (control, ⌃), shift (⇧), alt +#: (opt, option, ⌥), super (cmd, command, ⌘). See also: GLFW mods +#: + +#: On Linux you can also use XKB key names to bind keys that are not +#: supported by GLFW. See XKB keys +#: for a list of key names. The name to use is the part +#: after the XKB_KEY_ prefix. Note that you can only use an XKB key +#: name for keys that are not known as GLFW keys. + +#: Finally, you can use raw system key codes to map keys, again only +#: for keys that are not known as GLFW keys. To see the system key +#: code for a key, start kitty with the kitty --debug-input option, +#: kitty will output some debug text for every key event. In that text +#: look for native_code, the value of that becomes the key name in the +#: shortcut. For example: + +#: .. code-block:: none + +#: on_key_input: glfw key: 0x61 native_code: 0x61 action: PRESS mods: none text: 'a' + +#: Here, the key name for the A key is 0x61 and you can use it with:: + +#: map ctrl+0x61 something + +#: to map Ctrl+A to something. + +#: You can use the special action no_op to unmap a keyboard shortcut +#: that is assigned in the default configuration:: + +#: map kitty_mod+space no_op + +#: If you would like kitty to completely ignore a key event, not even +#: sending it to the program running in the terminal, map it to +#: discard_event:: + +#: map kitty_mod+f1 discard_event + +#: You can combine multiple actions to be triggered by a single +#: shortcut with combine action, using the syntax below:: + +#: map key combine action1 action2 action3 ... + +#: For example:: + +#: map kitty_mod+e combine : new_window : next_layout + +#: This will create a new window and switch to the next available +#: layout. + +#: You can use multi-key shortcuts with the syntax shown below:: + +#: map key1>key2>key3 action + +#: For example:: + +#: map ctrl+f>2 set_font_size 20 + +#: The full list of actions that can be mapped to key presses is +#: available here . + +# kitty_mod ctrl+shift + +#: Special modifier key alias for default shortcuts. You can change +#: the value of this option to alter all default shortcuts that use +#: kitty_mod. + +# clear_all_shortcuts no + +#: Remove all shortcut definitions up to this point. Useful, for +#: instance, to remove the default shortcuts. + +# action_alias + +#: E.g. action_alias launch_tab launch --type=tab --cwd=current + +#: Define action aliases to avoid repeating the same options in +#: multiple mappings. Aliases can be defined for any action and will +#: be expanded recursively. For example, the above alias allows you to +#: create mappings to launch a new tab in the current working +#: directory without duplication:: + +#: map f1 launch_tab vim +#: map f2 launch_tab emacs + +#: Similarly, to alias kitten invocation:: + +#: action_alias hints kitten hints --hints-offset=0 + +# kitten_alias + +#: E.g. kitten_alias hints hints --hints-offset=0 + +#: Like action_alias above, but specifically for kittens. Generally, +#: prefer to use action_alias. This option is a legacy version, +#: present for backwards compatibility. It causes all invocations of +#: the aliased kitten to be substituted. So the example above will +#: cause all invocations of the hints kitten to have the --hints- +#: offset=0 option applied. + +#: Clipboard {{{ + +#: Copy to clipboard + +map kitty_mod+c copy_to_clipboard +# map cmd+c copy_to_clipboard + +#:: There is also a copy_or_interrupt action that can be optionally +#:: mapped to Ctrl+C. It will copy only if there is a selection and +#:: send an interrupt otherwise. Similarly, +#:: copy_and_clear_or_interrupt will copy and clear the selection or +#:: send an interrupt if there is no selection. + +#: Paste from clipboard + +map kitty_mod+v paste_from_clipboard +# map cmd+v paste_from_clipboard + +#: Paste from selection + +# map kitty_mod+s paste_from_selection +# map shift+insert paste_from_selection + +#: Pass selection to program + +# map kitty_mod+o pass_selection_to_program + +#:: You can also pass the contents of the current selection to any +#:: program with pass_selection_to_program. By default, the system's +#:: open program is used, but you can specify your own, the selection +#:: will be passed as a command line argument to the program. For +#:: example:: + +#:: map kitty_mod+o pass_selection_to_program firefox + +#:: You can pass the current selection to a terminal program running +#:: in a new kitty window, by using the @selection placeholder:: + +#:: map kitty_mod+y new_window less @selection + +#: }}} + +#: Scrolling {{{ + +#: Scroll line up + +# map kitty_mod+up scroll_line_up +# map kitty_mod+k scroll_line_up +# map opt+cmd+page_up scroll_line_up +# map cmd+up scroll_line_up + +#: Scroll line down + +# map kitty_mod+down scroll_line_down +# map kitty_mod+j scroll_line_down +# map opt+cmd+page_down scroll_line_down +# map cmd+down scroll_line_down + +#: Scroll page up + +# map kitty_mod+page_up scroll_page_up +# map cmd+page_up scroll_page_up + +#: Scroll page down + +# map kitty_mod+page_down scroll_page_down +# map cmd+page_down scroll_page_down + +#: Scroll to top + +# map kitty_mod+home scroll_home +# map cmd+home scroll_home + +#: Scroll to bottom + +# map kitty_mod+end scroll_end +# map cmd+end scroll_end + +#: Scroll to previous shell prompt + +# map kitty_mod+z scroll_to_prompt -1 + +#:: Use a parameter of 0 for scroll_to_prompt to scroll to the last +#:: jumped to or the last clicked position. Requires shell +#:: integration +#:: to work. + +#: Scroll to next shell prompt + +# map kitty_mod+x scroll_to_prompt 1 + +#: Browse scrollback buffer in pager + +# map kitty_mod+h show_scrollback + +#:: You can pipe the contents of the current screen and history +#:: buffer as STDIN to an arbitrary program using launch --stdin- +#:: source. For example, the following opens the scrollback buffer in +#:: less in an overlay window:: + +#:: map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R + +#:: For more details on piping screen and buffer contents to external +#:: programs, see launch . + +#: Browse output of the last shell command in pager + +# map kitty_mod+g show_last_command_output + +#:: You can also define additional shortcuts to get the command +#:: output. For example, to get the first command output on screen:: + +#:: map f1 show_first_command_output_on_screen + +#:: To get the command output that was last accessed by a keyboard +#:: action or mouse action:: + +#:: map f1 show_last_visited_command_output + +#:: You can pipe the output of the last command run in the shell +#:: using the launch action. For example, the following opens the +#:: output in less in an overlay window:: + +#:: map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R + +#:: To get the output of the first command on the screen, use +#:: @first_cmd_output_on_screen. To get the output of the last jumped +#:: to command, use @last_visited_cmd_output. + +#:: Requires shell integration +#:: to work. + +#: }}} + +#: Window management {{{ + +#: New window + +map kitty_mod+enter launch --cwd=current +# map cmd+enter new_window + +#:: You can open a new kitty window running an arbitrary program, for +#:: example:: + +#:: map kitty_mod+y launch mutt + +#:: You can open a new window with the current working directory set +#:: to the working directory of the current window using:: + +#:: map ctrl+alt+enter launch --cwd=current + +#:: You can open a new window that is allowed to control kitty via +#:: the kitty remote control facility with launch --allow-remote- +#:: control. Any programs running in that window will be allowed to +#:: control kitty. For example:: + +#:: map ctrl+enter launch --allow-remote-control some_program + +#:: You can open a new window next to the currently active window or +#:: as the first window, with:: + +#:: map ctrl+n launch --location=neighbor +#:: map ctrl+f launch --location=first + +#:: For more details, see launch +#:: . + +#: New OS window + +# map kitty_mod+n new_os_window +# map cmd+n new_os_window + +#:: Works like new_window above, except that it opens a top-level OS +#:: window. In particular you can use new_os_window_with_cwd to open +#:: a window with the current working directory. + +#: Close window + +# map kitty_mod+w close_window +# map shift+cmd+d close_window + +#: Next window + +# map kitty_mod+] next_window + +#: Previous window + +# map kitty_mod+[ previous_window + +#: Move window forward + +# map kitty_mod+f move_window_forward + +#: Move window backward + +# map kitty_mod+b move_window_backward + +#: Move window to top + +# map kitty_mod+` move_window_to_top + +#: Start resizing window + +# map kitty_mod+r start_resizing_window +# map cmd+r start_resizing_window + +#: First window + +# map kitty_mod+1 first_window +# map cmd+1 first_window + +#: Second window + +# map kitty_mod+2 second_window +# map cmd+2 second_window + +#: Third window + +# map kitty_mod+3 third_window +# map cmd+3 third_window + +#: Fourth window + +# map kitty_mod+4 fourth_window +# map cmd+4 fourth_window + +#: Fifth window + +# map kitty_mod+5 fifth_window +# map cmd+5 fifth_window + +#: Sixth window + +# map kitty_mod+6 sixth_window +# map cmd+6 sixth_window + +#: Seventh window + +# map kitty_mod+7 seventh_window +# map cmd+7 seventh_window + +#: Eight window + +# map kitty_mod+8 eighth_window +# map cmd+8 eighth_window + +#: Ninth window + +# map kitty_mod+9 ninth_window +# map cmd+9 ninth_window + +#: Tenth window + +# map kitty_mod+0 tenth_window + +#: Visually select and focus window + +# map kitty_mod+f7 focus_visible_window + +#:: Display overlay numbers and alphabets on the window, and switch +#:: the focus to the window when you press the key. When there are +#:: only two windows, the focus will be switched directly without +#:: displaying the overlay. You can change the overlay characters and +#:: their order with option visual_window_select_characters. + +#: Visually swap window with another + +# map kitty_mod+f8 swap_with_window + +#:: Works like focus_visible_window above, but swaps the window. + +#: }}} + +#: Tab management {{{ + +#: Next tab + +# map kitty_mod+right next_tab +# map shift+cmd+] next_tab +# map ctrl+tab next_tab + +#: Previous tab + +# map kitty_mod+left previous_tab +# map shift+cmd+[ previous_tab +# map ctrl+shift+tab previous_tab + +#: New tab + +# map kitty_mod+t new_tab +map kitty_mod+t new_tab_with_cwd + +#: Close tab + +# map kitty_mod+q close_tab +# map cmd+w close_tab + +#: Close OS window + +# map shift+cmd+w close_os_window + +#: Move tab forward + +# map kitty_mod+. move_tab_forward + +#: Move tab backward + +# map kitty_mod+, move_tab_backward + +#: Set tab title + +# map kitty_mod+alt+t set_tab_title +# map shift+cmd+i set_tab_title + + +#: You can also create shortcuts to go to specific tabs, with 1 being +#: the first tab, 2 the second tab and -1 being the previously active +#: tab, and any number larger than the last tab being the last tab:: + +#: map ctrl+alt+1 goto_tab 1 +#: map ctrl+alt+2 goto_tab 2 + +#: Just as with new_window above, you can also pass the name of +#: arbitrary commands to run when using new_tab and new_tab_with_cwd. +#: Finally, if you want the new tab to open next to the current tab +#: rather than at the end of the tabs list, use:: + +# map ctrl+t new_tab_with_cwd +#: }}} + +#: Layout management {{{ + +#: Next layout + +# map kitty_mod+l next_layout + + +#: You can also create shortcuts to switch to specific layouts:: + +#: map ctrl+alt+t goto_layout tall +#: map ctrl+alt+s goto_layout stack + +#: Similarly, to switch back to the previous layout:: + +#: map ctrl+alt+p last_used_layout + +#: There is also a toggle_layout action that switches to the named +#: layout or back to the previous layout if in the named layout. +#: Useful to temporarily "zoom" the active window by switching to the +#: stack layout:: + +#: map ctrl+alt+z toggle_layout stack +#: }}} + +#: Font sizes {{{ + +#: You can change the font size for all top-level kitty OS windows at +#: a time or only the current one. + +#: Increase font size + +# map kitty_mod+equal change_font_size all +2.0 +# map kitty_mod+plus change_font_size all +2.0 +# map kitty_mod+kp_add change_font_size all +2.0 +# map cmd+plus change_font_size all +2.0 +# map cmd+equal change_font_size all +2.0 +# map shift+cmd+equal change_font_size all +2.0 + +#: Decrease font size + +# map kitty_mod+minus change_font_size all -2.0 +# map kitty_mod+kp_subtract change_font_size all -2.0 +# map cmd+minus change_font_size all -2.0 +# map shift+cmd+minus change_font_size all -2.0 + +#: Reset font size + +# map kitty_mod+backspace change_font_size all 0 +# map cmd+0 change_font_size all 0 + + +#: To setup shortcuts for specific font sizes:: + +#: map kitty_mod+f6 change_font_size all 10.0 + +#: To setup shortcuts to change only the current OS window's font +#: size:: + +#: map kitty_mod+f6 change_font_size current 10.0 +#: }}} + +#: Select and act on visible text {{{ + +#: Use the hints kitten to select text and either pass it to an +#: external program or insert it into the terminal or copy it to the +#: clipboard. + +#: Open URL + +# map kitty_mod+e open_url_with_hints + +#:: Open a currently visible URL using the keyboard. The program used +#:: to open the URL is specified in open_url_with. + +#: Insert selected path + +# map kitty_mod+p>f kitten hints --type path --program - + +#:: Select a path/filename and insert it into the terminal. Useful, +#:: for instance to run git commands on a filename output from a +#:: previous git command. + +#: Open selected path + +# map kitty_mod+p>shift+f kitten hints --type path + +#:: Select a path/filename and open it with the default open program. + +#: Insert selected line + +# map kitty_mod+p>l kitten hints --type line --program - + +#:: Select a line of text and insert it into the terminal. Useful for +#:: the output of things like: `ls -1`. + +#: Insert selected word + +# map kitty_mod+p>w kitten hints --type word --program - + +#:: Select words and insert into terminal. + +#: Insert selected hash + +# map kitty_mod+p>h kitten hints --type hash --program - + +#:: Select something that looks like a hash and insert it into the +#:: terminal. Useful with git, which uses SHA1 hashes to identify +#:: commits. + +#: Open the selected file at the selected line + +# map kitty_mod+p>n kitten hints --type linenum + +#:: Select something that looks like filename:linenum and open it in +#:: vim at the specified line number. + +#: Open the selected hyperlink + +# map kitty_mod+p>y kitten hints --type hyperlink + +#:: Select a hyperlink (i.e. a URL that has been marked as such by +#:: the terminal program, for example, by `ls --hyperlink=auto`). + + +#: The hints kitten has many more modes of operation that you can map +#: to different shortcuts. For a full description see hints kitten +#: . +#: }}} + +#: Miscellaneous {{{ + +#: Show documentation + +# map kitty_mod+f1 show_kitty_doc overview + +#: Toggle fullscreen + +# map kitty_mod+f11 toggle_fullscreen +# map ctrl+cmd+f toggle_fullscreen + +#: Toggle maximized + +# map kitty_mod+f10 toggle_maximized + +#: Toggle macOS secure keyboard entry + +# map opt+cmd+s toggle_macos_secure_keyboard_entry + +#: Unicode input + +# map kitty_mod+u kitten unicode_input +# map ctrl+cmd+space kitten unicode_input + +#: Edit config file + +# map kitty_mod+f2 edit_config_file +# map cmd+, edit_config_file + +#: Open the kitty command shell + +# map kitty_mod+escape kitty_shell window + +#:: Open the kitty shell in a new window / tab / overlay / os_window +#:: to control kitty using commands. + +#: Increase background opacity + +# map kitty_mod+a>m set_background_opacity +0.1 + +#: Decrease background opacity + +# map kitty_mod+a>l set_background_opacity -0.1 + +#: Make background fully opaque + +# map kitty_mod+a>1 set_background_opacity 1 + +#: Reset background opacity + +# map kitty_mod+a>d set_background_opacity default + +#: Reset the terminal + +# map kitty_mod+delete clear_terminal reset active +# map opt+cmd+r clear_terminal reset active + +#:: You can create shortcuts to clear/reset the terminal. For +#:: example:: + +#:: # Reset the terminal +#:: map f1 clear_terminal reset active +#:: # Clear the terminal screen by erasing all contents +#:: map f1 clear_terminal clear active +#:: # Clear the terminal scrollback by erasing it +#:: map f1 clear_terminal scrollback active +#:: # Scroll the contents of the screen into the scrollback +#:: map f1 clear_terminal scroll active +#:: # Clear everything up to the line with the cursor +#:: map f1 clear_terminal to_cursor active + +#:: If you want to operate on all kitty windows instead of just the +#:: current one, use all instead of active. + +#:: It is also possible to remap Ctrl+L to both scroll the current +#:: screen contents into the scrollback buffer and clear the screen, +#:: instead of just clearing the screen, for example, for ZSH add the +#:: following to ~/.zshrc: + +#:: .. code-block:: zsh + +#:: scroll-and-clear-screen() { +#:: printf '\n%.0s' {1..$LINES} +#:: zle clear-screen +#:: } +#:: zle -N scroll-and-clear-screen +#:: bindkey '^l' scroll-and-clear-screen + +#: Clear up to cursor line + +# map cmd+k clear_terminal to_cursor active + +#: Reload kitty.conf + +# map kitty_mod+f5 load_config_file +# map ctrl+cmd+, load_config_file + +#:: Reload kitty.conf, applying any changes since the last time it +#:: was loaded. Note that a handful of options cannot be dynamically +#:: changed and require a full restart of kitty. Particularly, when +#:: changing shortcuts for actions located on the macOS global menu +#:: bar, a full restart is needed. You can also map a keybinding to +#:: load a different config file, for example:: + +#:: map f5 load_config /path/to/alternative/kitty.conf + +#:: Note that all options from the original kitty.conf are discarded, +#:: in other words the new configuration *replace* the old ones. + +#: Debug kitty configuration + +# map kitty_mod+f6 debug_config +# map opt+cmd+, debug_config + +#:: Show details about exactly what configuration kitty is running +#:: with and its host environment. Useful for debugging issues. + +#: Send arbitrary text on key presses + +#:: E.g. map ctrl+shift+alt+h send_text all Hello World + +#:: You can tell kitty to send arbitrary (UTF-8) encoded text to the +#:: client program when pressing specified shortcut keys. For +#:: example:: + +#:: map ctrl+alt+a send_text all Special text + +#:: This will send "Special text" when you press the Ctrl+Alt+A key +#:: combination. The text to be sent decodes ANSI C escapes +#:: so you can use escapes like \e to send control +#:: codes or \u21fb to send Unicode characters (or you can just input +#:: the Unicode characters directly as UTF-8 text). You can use +#:: `kitty +kitten show_key` to get the key escape codes you want to +#:: emulate. + +#:: The first argument to send_text is the keyboard modes in which to +#:: activate the shortcut. The possible values are normal, +#:: application, kitty or a comma separated combination of them. The +#:: modes normal and application refer to the DECCKM cursor key mode +#:: for terminals, and kitty refers to the kitty extended keyboard +#:: protocol. The special value all means all of them. + +#:: Some more examples:: + +#:: # Output a word and move the cursor to the start of the line (like typing and pressing Home) +#:: map ctrl+alt+a send_text normal Word\e[H +#:: map ctrl+alt+a send_text application Word\eOH +#:: # Run a command at a shell prompt (like typing the command and pressing Enter) +#:: map ctrl+alt+a send_text normal,application some command with arguments\r + +#: Open kitty Website + +# map shift+cmd+/ open_url https://sw.kovidgoyal.net/kitty/ + +#: Hide macOS kitty application + +# map cmd+h hide_macos_app + +#: Hide macOS other applications + +# map opt+cmd+h hide_macos_other_apps + +#: Minimize macOS window + +# map cmd+m minimize_macos_window + +#: Quit kitty + +# map cmd+q quit + +#: }}} + +#: }}} +# +# map ctrl+f launch --type=overlay --stdin-source=@screen_scrollback /bin/sh -c "/usr/local/bin/fzf --no-sort --no-mouse --exact -i --tac | kitty +kitten clipboard" + +map cmd+/ launch --allow-remote-control kitty +kitten kitty_search/search.py @active-kitty-window-id + +# Add or uncomment the following line to enable shell integration: + +enable_kitty_shell_integration source ~/.zshrc + +# BEGIN_KITTY_THEME +# Tomorrow Night Bright +include current-theme.conf +# END_KITTY_THEME diff --git a/macprefs b/macprefs new file mode 100644 index 0000000..60255f8 --- /dev/null +++ b/macprefs @@ -0,0 +1,28 @@ + +// Remove login desktop picture for quicker startup +defaults write /Library/Preferences/com.apple.loginwindow DesktopPicture "" + +// Enable text selection in quick view window +defaults write com.apple.finder QLEnableTextSelection -bool true && killall Finder + +// you can adjust the animation speeds by changing the number after the -float flag。 +defaults write com.apple.dock expose-animation-duration -float 0.15 && killall Dock + + +// "Differentiate Without Color" accessibility setting. This setting changes the user interface to not rely on color to convey information, which can reduce the complexity of the display and potentially improve performance. +defaults write com.apple.Accessibility DifferentiateWithoutColor -int 1 + +// "Reduce Motion" accessibility setting. This setting reduces the use of animation and certain visual effects in macOS. This can help improve performance by reducing the demand on your Mac's graphics processing. +defaults write com.apple.Accessibility ReduceMotionEnabled -int 1 + +// Same thing as the previous command, but for a different system setting. It's a bit redundant, but it doesn't hurt to include it. +defaults write com.apple.universalaccess reduceMotion -int 1 + +// "Reduce Transparency" accessibility setting. This setting reduces the use of transparency and blur effects throughout macOS, which can also help improve performance by reducing the demand on your Mac's graphics processing. +defaults write com.apple.universalaccess reduceTransparency -int 1 + +// Remove the auto-hide Dock delay +defaults write com.apple.dock autohide-delay -float 0 + +// Apply the changes immediately +killall Dock diff --git a/zsh/antidote/.zsh_plugins.txt b/zsh/antidote/.zsh_plugins.txt new file mode 100644 index 0000000..ed9b3ce --- /dev/null +++ b/zsh/antidote/.zsh_plugins.txt @@ -0,0 +1,32 @@ +# .zsh_plugins.txt + +getantidote/use-omz +ohmyzsh/ohmyzsh path:lib + +# Default oh-my-zsh plugins +ohmyzsh/ohmyzsh path:plugins/brew +ohmyzsh/ohmyzsh path:plugins/colorize +ohmyzsh/ohmyzsh path:plugins/docker +ohmyzsh/ohmyzsh path:plugins/dotenv +ohmyzsh/ohmyzsh path:plugins/fzf +ohmyzsh/ohmyzsh path:plugins/git +ohmyzsh/ohmyzsh path:plugins/git-extras +ohmyzsh/ohmyzsh path:plugins/gh +ohmyzsh/ohmyzsh path:plugins/man +ohmyzsh/ohmyzsh path:plugins/node +ohmyzsh/ohmyzsh path:plugins/nvm +ohmyzsh/ohmyzsh path:plugins/pip +ohmyzsh/ohmyzsh path:plugins/python +ohmyzsh/ohmyzsh path:plugins/tmux +ohmyzsh/ohmyzsh path:plugins/tmuxinator +ohmyzsh/ohmyzsh path:plugins/virtualenv +ohmyzsh/ohmyzsh path:plugins/zsh-interactive-cd + + +# External plugins +therealklanni/purity +zsh-users/zsh-completions +zsh-users/zsh-autosuggestions +zsh-users/zsh-syntax-highlighting +# marlonrichert/zsh-autocomplete +jeffreytse/zsh-vi-mode diff --git a/zsh/install b/zsh/install index 9e46cfc..3d7b3f2 100755 --- a/zsh/install +++ b/zsh/install @@ -31,5 +31,8 @@ function init() { init +# ln -s ~/.dotfiles/zsh/antidote/.zsh_plugins.txt ~/.zsh_plugins.txt + + From b50147cffc9964092c18b10848020f59b65a6ab6 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 18 Jul 2024 13:13:55 -0700 Subject: [PATCH 23/25] Update zsh configurations and path settings --- zsh/.zprofile | 2 +- zsh/.zshenv | 2 + zsh/.zshrc | 52 ++++++++++--------- zsh/README.md | 25 +++++---- zsh/{ => archive}/.antigenrc | 0 zsh/archive/antigen/init.zsh | 98 ++++++++++++++++++++++++++++++++++++ zsh/install | 47 +++++++++++------ 7 files changed, 175 insertions(+), 51 deletions(-) rename zsh/{ => archive}/.antigenrc (100%) create mode 100644 zsh/archive/antigen/init.zsh diff --git a/zsh/.zprofile b/zsh/.zprofile index c6363ea..4e0da26 100644 --- a/zsh/.zprofile +++ b/zsh/.zprofile @@ -3,7 +3,7 @@ export CONFIG="$HOME/conf" #export LESSOPEN="|/usr/local/bin/lesspipe.sh %s" LESS_ADVANCED_PREPROCESSOR=1 # Created by `pipx` on 2022-07-02 11:27:20 -export PATH="$PATH:/Users/jon/.local/bin" +export PATH="$PATH:$HOME/.local/bin" if [[ $OSTYPE == 'darwin'* ]]; then # eval $(/opt/homebrew/bin/brew shellenv) diff --git a/zsh/.zshenv b/zsh/.zshenv index 160497c..f64929f 100644 --- a/zsh/.zshenv +++ b/zsh/.zshenv @@ -1,3 +1,5 @@ if [[ "$SHLVL" -eq 1 && ! -o LOGIN && -s "${ZDOTDIR:-$HOME}/.zprofile" ]]; then source "${ZDOTDIR:-$HOME}/.zprofile" fi +fpath=( "$HOME/.zfunctions" $fpath ) + diff --git a/zsh/.zshrc b/zsh/.zshrc index 9956c0e..7128737 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -1,29 +1,35 @@ +# Used on Intel Macs -# Time the stuff. -integer t0=$(date '+%s') +# Autoload compinit and promptinit +autoload -Uz compinit promptinit -source $HOME/antigen.zsh +# Use brew to install brew install antidote +source $(brew --prefix)/opt/antidote/share/antidote/antidote.zsh -autoload -U promptinit; promptinit -# Load Antigen configurations -antigen init ~/.antigenrc -# prompt pure -# prompt purity +antidote load - -function { - local -i t1 startup - t1=$(date '+%s') - startup=$(( t1 - t0 )) - [[ $startup -gt 1 ]] && print "Hmm, poor shell startup time: $startup" - #print "startup time: $startup" -} -unset t0 +bindkey -r '\C-s' +stty -ixon [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh -export OPENAI_API_KEY='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' -export PATH="/usr/local/opt/binutils/bin:$PATH" -export PATH=/Users/jon/.meteor:$PATH -# export PATH="/usr/local/opt/openssl@3/bin:$PATH" -export VOLTA_HOME="$HOME/.volta" -export PATH="$VOLTA_HOME/bin:$PATH" + +# Load private zsh configurations +if [[ -f ~/.zsh_private ]]; then + source ~/.zsh_private +fi + +# Update PATH +export PATH="/usr/local/opt/binutils/bin:/usr/local/sbin:/usr/local/bin:$HOME/Library/pnpm:$PATH" + +export TERM=xterm + +bindkey '^[[A' autosuggest-accept +bindkey '^[[B' autosuggest-accept + +. "$HOME/.asdf/asdf.sh" + +# append completions to fpath +fpath=(${ASDF_DIR}/completions $fpath) + +# initialise completions with ZSH's compinit +compinit diff --git a/zsh/README.md b/zsh/README.md index f7fd3b9..d7f5fa0 100644 --- a/zsh/README.md +++ b/zsh/README.md @@ -1,16 +1,15 @@ -`$ chmod +x install` -`$ ./install` +# Install -# Modify pam.d file to change shell if coming from bash -$ sudo vim /etc/pam.d/chsh +* `$ chmod +x install` +* `$ ./install` -Replace -``` -auth required pam_shells.so -``` +## Execution Order -with - -``` -auth sufficient pam_shells.so -``` +* /etc/zshenv +* ~/.zshenv +* /etc/zprofile (if this is a login shell) +* ~/.zprofile (if this is a login shell) +* /etc/zshrc (if this is an interactive shell) +* ~/.zshrc (if this is an interactive shell) +* /etc/zlogin (if this is a login shell) +* ~/.zlogin (if this is a login shell) diff --git a/zsh/.antigenrc b/zsh/archive/.antigenrc similarity index 100% rename from zsh/.antigenrc rename to zsh/archive/.antigenrc diff --git a/zsh/archive/antigen/init.zsh b/zsh/archive/antigen/init.zsh new file mode 100644 index 0000000..5d125e3 --- /dev/null +++ b/zsh/archive/antigen/init.zsh @@ -0,0 +1,98 @@ +#-- START ZCACHE GENERATED FILE +#-- GENERATED: Fri Mar 10 00:14:02 PST 2023 +#-- ANTIGEN v2.2.2 +_antigen () { + local -a _1st_arguments + _1st_arguments=('apply:Load all bundle completions' 'bundle:Install and load the given plugin' 'bundles:Bulk define bundles' 'cleanup:Clean up the clones of repos which are not used by any bundles currently loaded' 'cache-gen:Generate cache' 'init:Load Antigen configuration from file' 'list:List out the currently loaded bundles' 'purge:Remove a cloned bundle from filesystem' 'reset:Clears cache' 'restore:Restore the bundles state as specified in the snapshot' 'revert:Revert the state of all bundles to how they were before the last antigen update' 'selfupdate:Update antigen itself' 'snapshot:Create a snapshot of all the active clones' 'theme:Switch the prompt theme' 'update:Update all bundles' 'use:Load any (supported) zsh pre-packaged framework') + _1st_arguments+=('help:Show this message' 'version:Display Antigen version') + __bundle () { + _arguments '--loc[Path to the location ]' '--url[Path to the repository ]' '--branch[Git branch name]' '--no-local-clone[Do not create a clone]' + } + __list () { + _arguments '--simple[Show only bundle name]' '--short[Show only bundle name and branch]' '--long[Show bundle records]' + } + __cleanup () { + _arguments '--force[Do not ask for confirmation]' + } + _arguments '*:: :->command' + if (( CURRENT == 1 )) + then + _describe -t commands "antigen command" _1st_arguments + return + fi + local -a _command_args + case "$words[1]" in + (bundle) __bundle ;; + (use) compadd "$@" "oh-my-zsh" "prezto" ;; + (cleanup) __cleanup ;; + (update|purge) compadd $(type -f \-antigen-get-bundles &> /dev/null || antigen &> /dev/null; -antigen-get-bundles --simple 2> /dev/null) ;; + (theme) compadd $(type -f \-antigen-get-themes &> /dev/null || antigen &> /dev/null; -antigen-get-themes 2> /dev/null) ;; + (list) __list ;; + esac +} +antigen () { + local MATCH MBEGIN MEND + [[ "$ZSH_EVAL_CONTEXT" =~ "toplevel:*" || "$ZSH_EVAL_CONTEXT" =~ "cmdarg:*" ]] && source "/Users/jon/antigen.zsh" && eval antigen $@; + return 0; +} +typeset -gaU fpath path +fpath+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) path+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) +_antigen_compinit () { + autoload -Uz compinit; compinit -d "/Users/jon/.antigen/.zcompdump"; compdef _antigen antigen + add-zsh-hook -D precmd _antigen_compinit +} +autoload -Uz add-zsh-hook; add-zsh-hook precmd _antigen_compinit +compdef () {} + +if [[ -n "/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh" ]]; then + ZSH="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh"; ZSH_CACHE_DIR="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/cache/" +fi +#--- BUNDLES BEGIN +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh'; +source '/Users/jon/.antigen/bundles/therealklanni/purity/purity.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh'; +source '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm/zsh-nvm.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh'; +source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh'; +source '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases/zsh-docker-compose-aliases.zsh'; +source '/Users/jon/.antigen/bundles/webyneter/docker-aliases/docker-aliases.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-completions/zsh-completions.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh'; +source '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh'; + +#--- BUNDLES END +typeset -gaU _ANTIGEN_BUNDLE_RECORD; _ANTIGEN_BUNDLE_RECORD=('https://github.com/robbyrussell/oh-my-zsh.git lib plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/macos plugin true' 'https://github.com/therealklanni/purity.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/git plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/command-not-found plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/docker plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/heroku plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/pip plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/kubernetes plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/lein plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/fzf plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/httpie plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/mosh plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/npm plugin true' 'https://github.com/lukechilds/zsh-nvm.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copyfile plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copypath plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/yarn plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/alias-finder plugin true' 'https://github.com/luismayta/zsh-docker-compose-aliases.git / plugin true' 'https://github.com/webyneter/docker-aliases.git / plugin true' 'https://github.com/zsh-users/zsh-completions.git / plugin true' 'https://github.com/zsh-users/zsh-autosuggestions.git / plugin true' 'https://github.com/zsh-users/zsh-syntax-highlighting.git / plugin true') +typeset -g _ANTIGEN_CACHE_LOADED; _ANTIGEN_CACHE_LOADED=true +typeset -ga _ZCACHE_BUNDLE_SOURCE; _ZCACHE_BUNDLE_SOURCE=('/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh' '/Users/jon/.antigen/bundles/therealklanni/purity//' '/Users/jon/.antigen/bundles/therealklanni/purity///purity.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/kubernetes' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm//' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm///zsh-nvm.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases//' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases///zsh-docker-compose-aliases.zsh' '/Users/jon/.antigen/bundles/webyneter/docker-aliases//' '/Users/jon/.antigen/bundles/webyneter/docker-aliases///docker-aliases.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions///zsh-completions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions///zsh-autosuggestions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting//' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting///zsh-syntax-highlighting.plugin.zsh') +typeset -g _ANTIGEN_CACHE_VERSION; _ANTIGEN_CACHE_VERSION='v2.2.2' + +#-- END ZCACHE GENERATED FILE diff --git a/zsh/install b/zsh/install index 3d7b3f2..17212e2 100755 --- a/zsh/install +++ b/zsh/install @@ -1,5 +1,3 @@ -#!/bin/bash - # Switch to zsh export REPO="$(git rev-parse --show-toplevel)" @@ -9,30 +7,51 @@ if [ -n "`$SHELL -c 'echo $ZSH_VERSION'`" ]; then echo "zsh detected" elif [ -n "`$SHELL -c 'echo $BASH_VERSION'`" ]; then # assume Bash - sudo chsh -s $(which zsh) $(whoami) + chsh -s $(which zsh) else echo "Something's not right..." exit 0 - fi function init() { - sudo apt-get install zsh - sudo apt-get install zsh-autosuggestions zsh-syntax-highlighting + # Check if Homebrew is installed, install if we don't have it + if test ! $(which brew); then + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi - mkdir $HOME/antigen - curl -L git.io/antigen > $HOME/antigen/antigen.zsh + # Install zsh and zsh related tools + brew install zsh zsh-autosuggestions zsh-syntax-highlighting - cp $REPO/zsh/.zshrc $REPO/zsh/.zprofile $REPO/zsh/.zshenv $REPO/zsh/.antigenrc $HOME/ + # Check if Antidote is installed, install if we don't have it + if ! brew ls --versions antidote > /dev/null; then + brew install antidote + fi - git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf - ~/.fzf/install -} + # Copy zsh configurations to home directory + cp $REPO/zsh/.zshrc $REPO/zsh/.zprofile $REPO/zsh/.zshenv $HOME/ -init + # If .zsh_private does not exist, create it + if [ ! -f $HOME/.zsh_private ]; then + touch $HOME/.zsh_private + fi -# ln -s ~/.dotfiles/zsh/antidote/.zsh_plugins.txt ~/.zsh_plugins.txt + # Copy .zsh_plugins.txt to home directory + cp $REPO/zsh/antidote/.zsh_plugins.txt $HOME/ + # Create a symbolic link to .zsh_plugins.txt in the home directory + ln -s $HOME/.zsh_plugins.txt $HOME/.zsh_plugins.txt + # If .zsh_plugins.txt does not exist, create it + if [ ! -f $HOME/.zsh_plugins.txt ]; then + touch $HOME/.zsh_plugins.txt + fi + # Update PATH + echo 'export PATH="/usr/local/opt/binutils/bin:/usr/local/sbin:/usr/local/bin:'$HOME'/Library/pnpm:$PATH"' >> $HOME/.zshrc + # Install fzf + git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf + ~/.fzf/install +} + +init From ef8fb07c057f2273ba58f44aeab04fc333ed8ced Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 18 Jul 2024 13:14:18 -0700 Subject: [PATCH 24/25] sync kitty --- kitty/kitty.conf | 2298 +--------------------------------------------- 1 file changed, 1 insertion(+), 2297 deletions(-) mode change 100644 => 120000 kitty/kitty.conf diff --git a/kitty/kitty.conf b/kitty/kitty.conf deleted file mode 100644 index 7d5a7f9..0000000 --- a/kitty/kitty.conf +++ /dev/null @@ -1,2297 +0,0 @@ -# vim:fileencoding=utf-8:foldmethod=marker - -#: Fonts {{{ - -#: kitty has very powerful font management. You can configure -#: individual font faces and even specify special fonts for particular -#: characters. - -# font_family monospace -# bold_font auto -# italic_font auto -# bold_italic_font auto - -#: You can specify different fonts for the bold/italic/bold-italic -#: variants. To get a full list of supported fonts use the `kitty -#: +list-fonts` command. By default they are derived automatically, by -#: the OSes font system. When bold_font or bold_italic_font is set to -#: auto on macOS, the priority of bold fonts is semi-bold, bold, -#: heavy. Setting them manually is useful for font families that have -#: many weight variants like Book, Medium, Thick, etc. For example:: - -#: font_family Operator Mono Book -#: bold_font Operator Mono Medium -#: italic_font Operator Mono Book Italic -#: bold_italic_font Operator Mono Medium Italic - -# font_size 11.0 - -#: Font size (in pts) - -# force_ltr no - -#: kitty does not support BIDI (bidirectional text), however, for RTL -#: scripts, words are automatically displayed in RTL. That is to say, -#: in an RTL script, the words "HELLO WORLD" display in kitty as -#: "WORLD HELLO", and if you try to select a substring of an RTL- -#: shaped string, you will get the character that would be there had -#: the the string been LTR. For example, assuming the Hebrew word -#: ירושלים, selecting the character that on the screen appears to be ם -#: actually writes into the selection buffer the character י. kitty's -#: default behavior is useful in conjunction with a filter to reverse -#: the word order, however, if you wish to manipulate RTL glyphs, it -#: can be very challenging to work with, so this option is provided to -#: turn it off. Furthermore, this option can be used with the command -#: line program GNU FriBidi -#: to get BIDI -#: support, because it will force kitty to always treat the text as -#: LTR, which FriBidi expects for terminals. - -# symbol_map - -#: E.g. symbol_map U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols - -#: Map the specified Unicode codepoints to a particular font. Useful -#: if you need special rendering for some symbols, such as for -#: Powerline. Avoids the need for patched fonts. Each Unicode code -#: point is specified in the form `U+`. You -#: can specify multiple code points, separated by commas and ranges -#: separated by hyphens. This option can be specified multiple times. -#: The syntax is:: - -#: symbol_map codepoints Font Family Name - -# narrow_symbols - -#: E.g. narrow_symbols U+E0A0-U+E0A3,U+E0C0-U+E0C7 1 - -#: Usually, for Private Use Unicode characters and some symbol/dingbat -#: characters, if the character is followed by one or more spaces, -#: kitty will use those extra cells to render the character larger, if -#: the character in the font has a wide aspect ratio. Using this -#: option you can force kitty to restrict the specified code points to -#: render in the specified number of cells (defaulting to one cell). -#: This option can be specified multiple times. The syntax is:: - -#: narrow_symbols codepoints [optionally the number of cells] - -# disable_ligatures never - -#: Choose how you want to handle multi-character ligatures. The -#: default is to always render them. You can tell kitty to not render -#: them when the cursor is over them by using cursor to make editing -#: easier, or have kitty never render them at all by using always, if -#: you don't like them. The ligature strategy can be set per-window -#: either using the kitty remote control facility or by defining -#: shortcuts for it in kitty.conf, for example:: - -#: map alt+1 disable_ligatures_in active always -#: map alt+2 disable_ligatures_in all never -#: map alt+3 disable_ligatures_in tab cursor - -#: Note that this refers to programming ligatures, typically -#: implemented using the calt OpenType feature. For disabling general -#: ligatures, use the font_features option. - -# font_features - -#: E.g. font_features none - -#: Choose exactly which OpenType features to enable or disable. This -#: is useful as some fonts might have features worthwhile in a -#: terminal. For example, Fira Code includes a discretionary feature, -#: zero, which in that font changes the appearance of the zero (0), to -#: make it more easily distinguishable from Ø. Fira Code also includes -#: other discretionary features known as Stylistic Sets which have the -#: tags ss01 through ss20. - -#: For the exact syntax to use for individual features, see the -#: HarfBuzz documentation . - -#: Note that this code is indexed by PostScript name, and not the font -#: family. This allows you to define very precise feature settings; -#: e.g. you can disable a feature in the italic font but not in the -#: regular font. - -#: On Linux, font features are first read from the FontConfig database -#: and then this option is applied, so they can be configured in a -#: single, central place. - -#: To get the PostScript name for a font, use `kitty +list-fonts -#: --psnames`: - -#: .. code-block:: sh - -#: $ kitty +list-fonts --psnames | grep Fira -#: Fira Code -#: Fira Code Bold (FiraCode-Bold) -#: Fira Code Light (FiraCode-Light) -#: Fira Code Medium (FiraCode-Medium) -#: Fira Code Regular (FiraCode-Regular) -#: Fira Code Retina (FiraCode-Retina) - -#: The part in brackets is the PostScript name. - -#: Enable alternate zero and oldstyle numerals:: - -#: font_features FiraCode-Retina +zero +onum - -#: Enable only alternate zero in the bold font:: - -#: font_features FiraCode-Bold +zero - -#: Disable the normal ligatures, but keep the calt feature which (in -#: this font) breaks up monotony:: - -#: font_features TT2020StyleB-Regular -liga +calt - -#: In conjunction with force_ltr, you may want to disable Arabic -#: shaping entirely, and only look at their isolated forms if they -#: show up in a document. You can do this with e.g.:: - -#: font_features UnifontMedium +isol -medi -fina -init - -# modify_font - -#: Modify font characteristics such as the position or thickness of -#: the underline and strikethrough. The modifications can have the -#: suffix px for pixels or % for percentage of original value. No -#: suffix means use pts. For example:: - -#: modify_font underline_position -2 -#: modify_font underline_thickness 150% -#: modify_font strikethrough_position 2px - -#: Additionally, you can modify the size of the cell in which each -#: font glyph is rendered and the baseline at which the glyph is -#: placed in the cell. For example:: - -#: modify_font cell_width 80% -#: modify_font cell_height -2px -#: modify_font baseline 3 - -#: Note that modifying the baseline will automatically adjust the -#: underline and strikethrough positions by the same amount. -#: Increasing the baseline raises glyphs inside the cell and -#: decreasing it lowers them. Decreasing the cell size might cause -#: rendering artifacts, so use with care. - -# box_drawing_scale 0.001, 1, 1.5, 2 - -#: The sizes of the lines used for the box drawing Unicode characters. -#: These values are in pts. They will be scaled by the monitor DPI to -#: arrive at a pixel value. There must be four values corresponding to -#: thin, normal, thick, and very thick lines. - -# undercurl_style thin-sparse - -#: The style with which undercurls are rendered. This option takes the -#: form (thin|thick)-(sparse|dense). Thin and thick control the -#: thickness of the undercurl. Sparse and dense control how often the -#: curl oscillates. With sparse the curl will peak once per character, -#: with dense twice. - -#: }}} - -#: Cursor customization {{{ -# cursor #cccccc - -#: Default cursor color. If set to the special value none the cursor -#: will be rendered with a "reverse video" effect. It's color will be -#: the color of the text in the cell it is over and the text will be -#: rendered with the background color of the cell. Note that if the -#: program running in the terminal sets a cursor color, this takes -#: precedence. Also, the cursor colors are modified if the cell -#: background and foreground colors have very low contrast. -# cursor_text_color #111111 - -#: The color of text under the cursor. If you want it rendered with -#: the background color of the cell underneath instead, use the -#: special keyword: background. Note that if cursor is set to none -#: then this option is ignored. - -# cursor_shape block - -#: The cursor shape can be one of block, beam, underline. Note that -#: when reloading the config this will be changed only if the cursor -#: shape has not been set by the program running in the terminal. This -#: sets the default cursor shape, applications running in the terminal -#: can override it. In particular, shell integration -#: in kitty sets -#: the cursor shape to beam at shell prompts. You can avoid this by -#: setting shell_integration to no-cursor. - -# cursor_beam_thickness 1.5 - -#: The thickness of the beam cursor (in pts). - -# cursor_underline_thickness 2.0 - -#: The thickness of the underline cursor (in pts). - -# cursor_blink_interval -1 - -#: The interval to blink the cursor (in seconds). Set to zero to -#: disable blinking. Negative values mean use system default. Note -#: that the minimum interval will be limited to repaint_delay. - -# cursor_stop_blinking_after 15.0 - -#: Stop blinking cursor after the specified number of seconds of -#: keyboard inactivity. Set to zero to never stop blinking. - -#: }}} - -#: Scrollback {{{ - -# scrollback_lines 2000 - -#: Number of lines of history to keep in memory for scrolling back. -#: Memory is allocated on demand. Negative numbers are (effectively) -#: infinite scrollback. Note that using very large scrollback is not -#: recommended as it can slow down performance of the terminal and -#: also use large amounts of RAM. Instead, consider using -#: scrollback_pager_history_size. Note that on config reload if this -#: is changed it will only affect newly created windows, not existing -#: ones. - -# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER - -#: Program with which to view scrollback in a new window. The -#: scrollback buffer is passed as STDIN to this program. If you change -#: it, make sure the program you use can handle ANSI escape sequences -#: for colors and text formatting. INPUT_LINE_NUMBER in the command -#: line above will be replaced by an integer representing which line -#: should be at the top of the screen. Similarly CURSOR_LINE and -#: CURSOR_COLUMN will be replaced by the current cursor position or -#: set to 0 if there is no cursor, for example, when showing the last -#: command output. - -# scrollback_pager_history_size 0 - -#: Separate scrollback history size (in MB), used only for browsing -#: the scrollback buffer with pager. This separate buffer is not -#: available for interactive scrolling but will be piped to the pager -#: program when viewing scrollback buffer in a separate window. The -#: current implementation stores the data in UTF-8, so approximatively -#: 10000 lines per megabyte at 100 chars per line, for pure ASCII, -#: unformatted text. A value of zero or less disables this feature. -#: The maximum allowed size is 4GB. Note that on config reload if this -#: is changed it will only affect newly created windows, not existing -#: ones. - -# scrollback_fill_enlarged_window no - -#: Fill new space with lines from the scrollback buffer after -#: enlarging a window. - -# wheel_scroll_multiplier 5.0 - -#: Multiplier for the number of lines scrolled by the mouse wheel. -#: Note that this is only used for low precision scrolling devices, -#: not for high precision scrolling devices on platforms such as macOS -#: and Wayland. Use negative numbers to change scroll direction. See -#: also wheel_scroll_min_lines. - -# wheel_scroll_min_lines 1 - -#: The minimum number of lines scrolled by the mouse wheel. The scroll -#: multiplier wheel_scroll_multiplier only takes effect after it -#: reaches this number. Note that this is only used for low precision -#: scrolling devices like wheel mice that scroll by very small amounts -#: when using the wheel. With a negative number, the minimum number of -#: lines will always be added. - -# touch_scroll_multiplier 1.0 - -#: Multiplier for the number of lines scrolled by a touchpad. Note -#: that this is only used for high precision scrolling devices on -#: platforms such as macOS and Wayland. Use negative numbers to change -#: scroll direction. - -#: }}} - -#: Mouse {{{ - -# mouse_hide_wait 3.0 - -#: Hide mouse cursor after the specified number of seconds of the -#: mouse not being used. Set to zero to disable mouse cursor hiding. -#: Set to a negative value to hide the mouse cursor immediately when -#: typing text. Disabled by default on macOS as getting it to work -#: robustly with the ever-changing sea of bugs that is Cocoa is too -#: much effort. -# url_color #0087bd - url_style curly - -#: The color and style for highlighting URLs on mouse-over. url_style -#: can be one of: none, straight, double, curly, dotted, dashed. - - open_url_with default - -#: The program to open clicked URLs. The special value default with -#: first look for any URL handlers defined via the open_actions -#: facility and if non -#: are found, it will use the Operating System's default URL handler -#: (open on macOS and xdg-open on Linux). - -# url_prefixes file ftp ftps gemini git gopher http https irc ircs kitty mailto news sftp ssh - -#: The set of URL prefixes to look for when detecting a URL under the -#: mouse cursor. - - detect_urls yes - -#: Detect URLs under the mouse. Detected URLs are highlighted with an -#: underline and the mouse cursor becomes a hand over them. Even if -#: this option is disabled, URLs are still clickable. - -# url_excluded_characters - -#: Additional characters to be disallowed from URLs, when detecting -#: URLs under the mouse cursor. By default, all characters that are -#: legal in URLs are allowed. - -# show_hyperlink_targets no - -#: When the mouse hovers over a terminal hyperlink, show the actual -#: URL that will be activated when the hyperlink is clicked. - -# copy_on_select no - -#: Copy to clipboard or a private buffer on select. With this set to -#: clipboard, selecting text with the mouse will cause the text to be -#: copied to clipboard. Useful on platforms such as macOS that do not -#: have the concept of primary selection. You can instead specify a -#: name such as a1 to copy to a private kitty buffer. Map a shortcut -#: with the paste_from_buffer action to paste from this private -#: buffer. For example:: - -#: copy_on_select a1 - map shift+cmd+v paste_from_buffer a1 - -#: Note that copying to the clipboard is a security risk, as all -#: programs, including websites open in your browser can read the -#: contents of the system clipboard. - -# paste_actions quote-urls-at-prompt - -#: A comma separated list of actions to take when pasting text into -#: the terminal. The supported paste actions are: - -#: quote-urls-at-prompt: -#: If the text being pasted is a URL and the cursor is at a shell prompt, -#: automatically quote the URL (needs shell_integration). -#: confirm: -#: Confirm the paste if bracketed paste mode is not active or there is more -#: a large amount of text being pasted. -#: filter: -#: Run the filter_paste() function from the file paste-actions.py in -#: the kitty config directory on the pasted text. The text returned by the -#: function will be actually pasted. - -# strip_trailing_spaces never - -#: Remove spaces at the end of lines when copying to clipboard. A -#: value of smart will do it when using normal selections, but not -#: rectangle selections. A value of always will always do it. - -# select_by_word_characters @-./_~?&=%+# - -#: Characters considered part of a word when double clicking. In -#: addition to these characters any character that is marked as an -#: alphanumeric character in the Unicode database will be matched. - -# select_by_word_characters_forward - -#: Characters considered part of a word when extending the selection -#: forward on double clicking. In addition to these characters any -#: character that is marked as an alphanumeric character in the -#: Unicode database will be matched. - -#: If empty (default) select_by_word_characters will be used for both -#: directions. - -# click_interval -1.0 - -#: The interval between successive clicks to detect double/triple -#: clicks (in seconds). Negative numbers will use the system default -#: instead, if available, or fallback to 0.5. - -# focus_follows_mouse no - -#: Set the active window to the window under the mouse when moving the -#: mouse around. - -# pointer_shape_when_grabbed arrow - -#: The shape of the mouse pointer when the program running in the -#: terminal grabs the mouse. Valid values are: arrow, beam and hand. - -# default_pointer_shape beam - -#: The default shape of the mouse pointer. Valid values are: arrow, -#: beam and hand. - -# pointer_shape_when_dragging beam - -#: The default shape of the mouse pointer when dragging across text. -#: Valid values are: arrow, beam and hand. - -#: Mouse actions {{{ - -#: Mouse buttons can be mapped to perform arbitrary actions. The -#: syntax is: - -#: .. code-block:: none - -#: mouse_map button-name event-type modes action - -#: Where button-name is one of left, middle, right, b1 ... b8 with -#: added keyboard modifiers. For example: ctrl+shift+left refers to -#: holding the Ctrl+Shift keys while clicking with the left mouse -#: button. The value b1 ... b8 can be used to refer to up to eight -#: buttons on a mouse. - -#: event-type is one of press, release, doublepress, triplepress, -#: click, doubleclick. modes indicates whether the action is performed -#: when the mouse is grabbed by the program running in the terminal, -#: or not. The values are grabbed or ungrabbed or a comma separated -#: combination of them. grabbed refers to when the program running in -#: the terminal has requested mouse events. Note that the click and -#: double click events have a delay of click_interval to disambiguate -#: from double and triple presses. - -#: You can run kitty with the kitty --debug-input command line option -#: to see mouse events. See the builtin actions below to get a sense -#: of what is possible. - -#: If you want to unmap an action, map it to no_op. For example, to -#: disable opening of URLs with a plain click:: - -#: mouse_map left click ungrabbed no_op - -#: See all the mappable actions including mouse actions here -#: . - -#: .. note:: -#: Once a selection is started, releasing the button that started it will -#: automatically end it and no release event will be dispatched. - -# clear_all_mouse_actions no - -#: Remove all mouse action definitions up to this point. Useful, for -#: instance, to remove the default mouse actions. - -#: Click the link under the mouse or move the cursor - -# mouse_map left click ungrabbed mouse_handle_click selection link prompt - -#:: First check for a selection and if one exists do nothing. Then -#:: check for a link under the mouse cursor and if one exists, click -#:: it. Finally check if the click happened at the current shell -#:: prompt and if so, move the cursor to the click location. Note -#:: that this requires shell integration -#:: to work. - -#: Click the link under the mouse or move the cursor even when grabbed - -# mouse_map shift+left click grabbed,ungrabbed mouse_handle_click selection link prompt - -#:: Same as above, except that the action is performed even when the -#:: mouse is grabbed by the program running in the terminal. - -#: Click the link under the mouse cursor - -# mouse_map ctrl+shift+left release grabbed,ungrabbed mouse_handle_click link - -#:: Variant with Ctrl+Shift is present because the simple click based -#:: version has an unavoidable delay of click_interval, to -#:: disambiguate clicks from double clicks. - -#: Discard press event for link click - -# mouse_map ctrl+shift+left press grabbed discard_event - -#:: Prevent this press event from being sent to the program that has -#:: grabbed the mouse, as the corresponding release event is used to -#:: open a URL. - -#: Paste from the primary selection - -# mouse_map middle release ungrabbed paste_from_selection - -#: Start selecting text - -# mouse_map left press ungrabbed mouse_selection normal - -#: Start selecting text in a rectangle - -# mouse_map ctrl+alt+left press ungrabbed mouse_selection rectangle - -#: Select a word - -# mouse_map left doublepress ungrabbed mouse_selection word - -#: Select a line - -# mouse_map left triplepress ungrabbed mouse_selection line - -#: Select line from point - -# mouse_map ctrl+alt+left triplepress ungrabbed mouse_selection line_from_point - -#:: Select from the clicked point to the end of the line. - -#: Extend the current selection - -# mouse_map right press ungrabbed mouse_selection extend - -#:: If you want only the end of the selection to be moved instead of -#:: the nearest boundary, use move-end instead of extend. - -#: Paste from the primary selection even when grabbed - -# mouse_map shift+middle release ungrabbed,grabbed paste_selection -# mouse_map shift+middle press grabbed discard_event - -#: Start selecting text even when grabbed - -# mouse_map shift+left press ungrabbed,grabbed mouse_selection normal - -#: Start selecting text in a rectangle even when grabbed - -# mouse_map ctrl+shift+alt+left press ungrabbed,grabbed mouse_selection rectangle - -#: Select a word even when grabbed - -# mouse_map shift+left doublepress ungrabbed,grabbed mouse_selection word - -#: Select a line even when grabbed - -# mouse_map shift+left triplepress ungrabbed,grabbed mouse_selection line - -#: Select line from point even when grabbed - -# mouse_map ctrl+shift+alt+left triplepress ungrabbed,grabbed mouse_selection line_from_point - -#:: Select from the clicked point to the end of the line even when -#:: grabbed. - -#: Extend the current selection even when grabbed - -# mouse_map shift+right press ungrabbed,grabbed mouse_selection extend - -#: Show clicked command output in pager - -# mouse_map ctrl+shift+right press ungrabbed mouse_show_command_output - -#:: Requires shell integration -#:: to work. - -#: }}} - -#: }}} - -#: Performance tuning {{{ - -# repaint_delay 10 - -#: Delay between screen updates (in milliseconds). Decreasing it, -#: increases frames-per-second (FPS) at the cost of more CPU usage. -#: The default value yields ~100 FPS which is more than sufficient for -#: most uses. Note that to actually achieve 100 FPS, you have to -#: either set sync_to_monitor to no or use a monitor with a high -#: refresh rate. Also, to minimize latency when there is pending input -#: to be processed, this option is ignored. - -# input_delay 3 - -#: Delay before input from the program running in the terminal is -#: processed (in milliseconds). Note that decreasing it will increase -#: responsiveness, but also increase CPU usage and might cause flicker -#: in full screen programs that redraw the entire screen on each loop, -#: because kitty is so fast that partial screen updates will be drawn. - -# sync_to_monitor yes - -#: Sync screen updates to the refresh rate of the monitor. This -#: prevents screen tearing -#: when scrolling. -#: However, it limits the rendering speed to the refresh rate of your -#: monitor. With a very high speed mouse/high keyboard repeat rate, -#: you may notice some slight input latency. If so, set this to no. - -#: }}} - -#: Terminal bell {{{ - -# enable_audio_bell yes - -#: The audio bell. Useful to disable it in environments that require -#: silence. - -# visual_bell_duration 0.0 - -#: The visual bell duration (in seconds). Flash the screen when a bell -#: occurs for the specified number of seconds. Set to zero to disable. - -# visual_bell_color none - -#: The color used by visual bell. Set to none will fall back to -#: selection background color. If you feel that the visual bell is too -#: bright, you can set it to a darker color. - -# window_alert_on_bell yes - -#: Request window attention on bell. Makes the dock icon bounce on -#: macOS or the taskbar flash on linux. - -# bell_on_tab "🔔 " - -#: Some text or a Unicode symbol to show on the tab if a window in the -#: tab that does not have focus has a bell. If you want to use leading -#: or trailing spaces, surround the text with quotes. See -#: tab_title_template for how this is rendered. - -#: For backwards compatibility, values of yes, y and true are -#: converted to the default bell symbol and no, n, false and none are -#: converted to the empty string. - -# command_on_bell none - -#: Program to run when a bell occurs. The environment variable -#: KITTY_CHILD_CMDLINE can be used to get the program running in the -#: window in which the bell occurred. - -# bell_path none - -#: Path to a sound file to play as the bell sound. If set to none, the -#: system default bell sound is used. Must be in a format supported by -#: the operating systems sound API, such as WAV or OGA on Linux -#: (libcanberra) or AIFF, MP3 or WAV on macOS (NSSound) - -#: }}} - -#: Window layout {{{ - - remember_window_size yes - initial_window_width 640 - initial_window_height 400 - -#: If enabled, the OS Window size will be remembered so that new -#: instances of kitty will have the same size as the previous -#: instance. If disabled, the OS Window will initially have size -#: configured by initial_window_width/height, in pixels. You can use a -#: suffix of "c" on the width/height values to have them interpreted -#: as number of cells instead of pixels. - - enabled_layouts * - -#: The enabled window layouts. A comma separated list of layout names. -#: The special value all means all layouts. The first listed layout -#: will be used as the startup layout. Default configuration is all -#: layouts in alphabetical order. For a list of available layouts, see -#: the layouts . - -# window_resize_step_cells 2 -# window_resize_step_lines 2 - -#: The step size (in units of cell width/cell height) to use when -#: resizing kitty windows in a layout with the shortcut -#: start_resizing_window. The cells value is used for horizontal -#: resizing, and the lines value is used for vertical resizing. - -# window_border_width 0.5pt - -#: The width of window borders. Can be either in pixels (px) or pts -#: (pt). Values in pts will be rounded to the nearest number of pixels -#: based on screen resolution. If not specified, the unit is assumed -#: to be pts. Note that borders are displayed only when more than one -#: window is visible. They are meant to separate multiple windows. - - draw_minimal_borders yes - -#: Draw only the minimum borders needed. This means that only the -#: borders that separate the window from a neighbor are drawn. Note -#: that setting a non-zero window_margin_width overrides this and -#: causes all borders to be drawn. - -# window_margin_width 0 - -#: The window margin (in pts) (blank area outside the border). A -#: single value sets all four sides. Two values set the vertical and -#: horizontal sides. Three values set top, horizontal and bottom. Four -#: values set top, right, bottom and left. - -# single_window_margin_width -1 - -#: The window margin to use when only a single window is visible (in -#: pts). Negative values will cause the value of window_margin_width -#: to be used instead. A single value sets all four sides. Two values -#: set the vertical and horizontal sides. Three values set top, -#: horizontal and bottom. Four values set top, right, bottom and left. - -# window_padding_width 0 - -#: The window padding (in pts) (blank area between the text and the -#: window border). A single value sets all four sides. Two values set -#: the vertical and horizontal sides. Three values set top, horizontal -#: and bottom. Four values set top, right, bottom and left. - -# placement_strategy center - -#: When the window size is not an exact multiple of the cell size, the -#: cell area of the terminal window will have some extra padding on -#: the sides. You can control how that padding is distributed with -#: this option. Using a value of center means the cell area will be -#: placed centrally. A value of top-left means the padding will be -#: only at the bottom and right edges. -# active_border_color #00ff00 - -#: The color for the border of the active window. Set this to none to -#: not draw borders around the active window. -# inactive_border_color #cccccc - -#: The color for the border of inactive windows. - -# bell_border_color #ff5a00 - -#: The color for the border of inactive windows in which a bell has -#: occurred. - -# inactive_text_alpha 1.0 - -#: Fade the text in inactive windows by the specified amount (a number -#: between zero and one, with zero being fully faded). - -# hide_window_decorations no - -#: Hide the window decorations (title-bar and window borders) with -#: yes. On macOS, titlebar-only can be used to only hide the titlebar. -#: Whether this works and exactly what effect it has depends on the -#: window manager/operating system. Note that the effects of changing -#: this option when reloading config are undefined. - -# window_logo_path none - -#: Path to a logo image. Must be in PNG format. Relative paths are -#: interpreted relative to the kitty config directory. The logo is -#: displayed in a corner of every kitty window. The position is -#: controlled by window_logo_position. Individual windows can be -#: configured to have different logos either using the launch action -#: or the remote control facility. - -# window_logo_position bottom-right - -#: Where to position the window logo in the window. The value can be -#: one of: top-left, top, top-right, left, center, right, bottom-left, -#: bottom, bottom-right. - -# window_logo_alpha 0.5 - -#: The amount the logo should be faded into the background. With zero -#: being fully faded and one being fully opaque. - -# resize_debounce_time 0.1 - -#: The time to wait before redrawing the screen when a resize event is -#: received (in seconds). On platforms such as macOS, where the -#: operating system sends events corresponding to the start and end of -#: a resize, this number is ignored. - -# resize_draw_strategy static - -#: Choose how kitty draws a window while a resize is in progress. A -#: value of static means draw the current window contents, mostly -#: unchanged. A value of scale means draw the current window contents -#: scaled. A value of blank means draw a blank window. A value of size -#: means show the window size in cells. - -# resize_in_steps no - -#: Resize the OS window in steps as large as the cells, instead of -#: with the usual pixel accuracy. Combined with initial_window_width -#: and initial_window_height in number of cells, this option can be -#: used to keep the margins as small as possible when resizing the OS -#: window. Note that this does not currently work on Wayland. - -# visual_window_select_characters 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ - -#: The list of characters for visual window selection. For example, -#: for selecting a window to focus on with focus_visible_window. The -#: value should be a series of unique numbers or alphabets, case -#: insensitive, from the set [0-9A-Z]. Specify your preference as a -#: string of characters. - -# confirm_os_window_close -1 - -#: Ask for confirmation when closing an OS window or a tab with at -#: least this number of kitty windows in it by window manager (e.g. -#: clicking the window close button or pressing the operating system -#: shortcut to close windows) or by the close_tab action. A value of -#: zero disables confirmation. This confirmation also applies to -#: requests to quit the entire application (all OS windows, via the -#: quit action). Negative values are converted to positive ones, -#: however, with shell_integration enabled, using negative values -#: means windows sitting at a shell prompt are not counted, only -#: windows where some command is currently running. Note that if you -#: want confirmation when closing individual windows, you can map the -#: close_window_with_confirmation action. - -#: }}} - -#: Tab bar {{{ - -# tab_bar_edge bottom - -#: The edge to show the tab bar on, top or bottom. - -# tab_bar_margin_width 0.0 - -#: The margin to the left and right of the tab bar (in pts). - -# tab_bar_margin_height 0.0 0.0 - -#: The margin above and below the tab bar (in pts). The first number -#: is the margin between the edge of the OS Window and the tab bar. -#: The second number is the margin between the tab bar and the -#: contents of the current tab. - -# tab_bar_style fade - -#: The tab bar style, can be one of: - -#: fade -#: Each tab's edges fade into the background color. (See also tab_fade) -#: slant -#: Tabs look like the tabs in a physical file. -#: separator -#: Tabs are separated by a configurable separator. (See also -#: tab_separator) -#: powerline -#: Tabs are shown as a continuous line with "fancy" separators. -#: (See also tab_powerline_style) -#: custom -#: A user-supplied Python function called draw_tab is loaded from the file -#: tab_bar.py in the kitty config directory. For examples of how to -#: write such a function, see the functions named draw_tab_with_* in -#: kitty's source code: kitty/tab_bar.py. See also -#: this discussion -#: for examples from kitty users. -#: hidden -#: The tab bar is hidden. If you use this, you might want to create a mapping -#: for the select_tab action which presents you with a list of tabs and -#: allows for easy switching to a tab. - -# tab_bar_align left - -#: The horizontal alignment of the tab bar, can be one of: left, -#: center, right. - -# tab_bar_min_tabs 2 - -#: The minimum number of tabs that must exist before the tab bar is -#: shown. - -# tab_switch_strategy previous - -#: The algorithm to use when switching to a tab when the current tab -#: is closed. The default of previous will switch to the last used -#: tab. A value of left will switch to the tab to the left of the -#: closed tab. A value of right will switch to the tab to the right of -#: the closed tab. A value of last will switch to the right-most tab. - -# tab_fade 0.25 0.5 0.75 1 - -#: Control how each tab fades into the background when using fade for -#: the tab_bar_style. Each number is an alpha (between zero and one) -#: that controls how much the corresponding cell fades into the -#: background, with zero being no fade and one being full fade. You -#: can change the number of cells used by adding/removing entries to -#: this list. - -# tab_separator " ┇" - -#: The separator between tabs in the tab bar when using separator as -#: the tab_bar_style. - -# tab_powerline_style angled - -#: The powerline separator style between tabs in the tab bar when -#: using powerline as the tab_bar_style, can be one of: angled, -#: slanted, round. - -# tab_activity_symbol none - -#: Some text or a Unicode symbol to show on the tab if a window in the -#: tab that does not have focus has some activity. If you want to use -#: leading or trailing spaces, surround the text with quotes. See -#: tab_title_template for how this is rendered. - -# tab_title_max_length 0 - -#: The maximum number of cells that can be used to render the text in -#: a tab. A value of zero means that no limit is applied. - -# tab_title_template "{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}" - -#: A template to render the tab title. The default just renders the -#: title with optional symbols for bell and activity. If you wish to -#: include the tab-index as well, use something like: {index}:{title}. -#: Useful if you have shortcuts mapped for goto_tab N. If you prefer -#: to see the index as a superscript, use {sup.index}. All data -#: available is: - -#: title -#: The current tab title. -#: index -#: The tab index useable with goto_tab N goto_tab shortcuts. -#: layout_name -#: The current layout name. -#: num_windows -#: The number of windows in the tab. -#: num_window_groups -#: The number of window groups (not counting overlay windows) in the tab. -#: tab.active_wd -#: The working directory of the currently active window in the tab (expensive, -#: requires syscall). Use active_oldest_wd to get the directory of the oldest foreground process rather than the newest. -#: tab.active_exe -#: The name of the executable running in the foreground of the currently active window in the tab (expensive, -#: requires syscall). Use active_oldest_exe for the oldest foreground process. -#: max_title_length -#: The maximum title length available. - -#: Note that formatting is done by Python's string formatting -#: machinery, so you can use, for instance, {layout_name[:2].upper()} -#: to show only the first two letters of the layout name, upper-cased. -#: If you want to style the text, you can use styling directives, for -#: example: -#: `{fmt.fg.red}red{fmt.fg.tab}normal{fmt.bg._00FF00}greenbg{fmt.bg.tab}`. -#: Similarly, for bold and italic: -#: `{fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}`. -#: Note that for backward compatibility, if {bell_symbol} or -#: {activity_symbol} are not present in the template, they are -#: prepended to it. - -# active_tab_title_template none - -#: Template to use for active tabs. If not specified falls back to -#: tab_title_template. - -# active_tab_foreground #000 -# active_tab_background #eee -# active_tab_font_style bold-italic -# inactive_tab_foreground #444 -# inactive_tab_background #999 -# inactive_tab_font_style normal - -#: Tab bar colors and styles. - -# tab_bar_background none - -#: Background color for the tab bar. Defaults to using the terminal -#: background color. - -# tab_bar_margin_color none - -#: Color for the tab bar margin area. Defaults to using the terminal -#: background color for margins above and below the tab bar. For side -#: margins the default color is chosen to match the background color -#: of the neighboring tab. - -#: }}} - -#: Color scheme {{{ - -# foreground #dddddd -# background #000000 - -#: The foreground and background colors. - -# background_opacity 1.0 - -#: The opacity of the background. A number between zero and one, where -#: one is opaque and zero is fully transparent. This will only work if -#: supported by the OS (for instance, when using a compositor under -#: X11). Note that it only sets the background color's opacity in -#: cells that have the same background color as the default terminal -#: background, so that things like the status bar in vim, powerline -#: prompts, etc. still look good. But it means that if you use a color -#: theme with a background color in your editor, it will not be -#: rendered as transparent. Instead you should change the default -#: background color in your kitty config and not use a background -#: color in the editor color scheme. Or use the escape codes to set -#: the terminals default colors in a shell script to launch your -#: editor. Be aware that using a value less than 1.0 is a (possibly -#: significant) performance hit. If you want to dynamically change -#: transparency of windows, set dynamic_background_opacity to yes -#: (this is off by default as it has a performance cost). Changing -#: this option when reloading the config will only work if -#: dynamic_background_opacity was enabled in the original config. - -# background_image none - -#: Path to a background image. Must be in PNG format. - -# background_image_layout tiled - -#: Whether to tile, scale or clamp the background image. The value can -#: be one of tiled, mirror-tiled, scaled, clamped or centered. - -# background_image_linear no - -#: When background image is scaled, whether linear interpolation -#: should be used. - -# dynamic_background_opacity no - -#: Allow changing of the background_opacity dynamically, using either -#: keyboard shortcuts (increase_background_opacity and -#: decrease_background_opacity) or the remote control facility. -#: Changing this option by reloading the config is not supported. - -# background_tint 0.0 - -#: How much to tint the background image by the background color. This -#: option makes it easier to read the text. Tinting is done using the -#: current background color for each window. This option applies only -#: if background_opacity is set and transparent windows are supported -#: or background_image is set. - -# background_tint_gaps 1.0 - -#: How much to tint the background image at the window gaps by the -#: background color, after applying background_tint. Since this is -#: multiplicative with background_tint, it can be used to lighten the -#: tint over the window gaps for a *separated* look. - -# dim_opacity 0.75 - -#: How much to dim text that has the DIM/FAINT attribute set. One -#: means no dimming and zero means fully dimmed (i.e. invisible). - -# selection_foreground #000000 -# selection_background #fffacd - -#: The foreground and background colors for text selected with the -#: mouse. Setting both of these to none will cause a "reverse video" -#: effect for selections, where the selection will be the cell text -#: color and the text will become the cell background color. Setting -#: only selection_foreground to none will cause the foreground color -#: to be used unchanged. Note that these colors can be overridden by -#: the program running in the terminal. - -#: The color table {{{ - -#: The 256 terminal colors. There are 8 basic colors, each color has a -#: dull and bright version, for the first 16 colors. You can set the -#: remaining 240 colors as color16 to color255. - -# color0 #000000 -# color8 #767676 - -#: black - -# color1 #cc0403 -# color9 #f2201f - -#: red - -# color2 #19cb00 -# color10 #23fd00 - -#: green - -# color3 #cecb00 -# color11 #fffd00 - -#: yellow - -# color4 #0d73cc -# color12 #1a8fff - -#: blue - -# color5 #cb1ed1 -# color13 #fd28ff - -#: magenta - -# color6 #0dcdcd -# color14 #14ffff - -#: cyan - -# color7 #dddddd -# color15 #ffffff - -#: white - -# mark1_foreground black - -#: Color for marks of type 1 - -# mark1_background #98d3cb - -#: Color for marks of type 1 (light steel blue) - -# mark2_foreground black - -#: Color for marks of type 2 - -# mark2_background #f2dcd3 - -#: Color for marks of type 1 (beige) - -# mark3_foreground black - -#: Color for marks of type 3 - -# mark3_background #f274bc - -#: Color for marks of type 3 (violet) - -#: }}} - -#: }}} - -#: Advanced {{{ - -# shell . - -#: The shell program to execute. The default value of . means to use -#: whatever shell is set as the default shell for the current user. -#: Note that on macOS if you change this, you might need to add -#: --login and --interactive to ensure that the shell starts in -#: interactive mode and reads its startup rc files. - -# editor . - -#: The terminal based text editor (such as vim or nano) to use when -#: editing the kitty config file or similar tasks. - -#: The default value of . means to use the environment variables -#: VISUAL and EDITOR in that order. If these variables aren't set, -#: kitty will run your shell ($SHELL -l -i -c env) to see if your -#: shell startup rc files set VISUAL or EDITOR. If that doesn't work, -#: kitty will cycle through various known editors (vim, emacs, etc.) -#: and take the first one that exists on your system. - -# close_on_child_death no - -#: Close the window when the child process (shell) exits. With the -#: default value no, the terminal will remain open when the child -#: exits as long as there are still processes outputting to the -#: terminal (for example disowned or backgrounded processes). When -#: enabled with yes, the window will close as soon as the child -#: process exits. Note that setting it to yes means that any -#: background processes still using the terminal can fail silently -#: because their stdout/stderr/stdin no longer work. - -# remote_control_password - -#: Allow other programs to control kitty using passwords. This option -#: can be specified multiple times tpicko add multiple passwords. If no -#: passwords are present kitty will ask the user for permission if a -#: program tries to use remote control with a password. A password can -#: also *optionally* be associated with a set of allowed remote -#: control actions. For example:: - -#: remote_control_password "my passphrase" get-colors set-colors focus-window focus-tab - -#: Only the specified actions will be allowed when using this -#: password. Glob patterns can be used too, for example:: - -#: remote_control_password "my passphrase" set-tab-* resize-* - -#: To get a list of available actions, run:: - -#: kitty @ --help - -#: A set of actions to be allowed when no password is sent can be -#: specified by using an empty password, for example:: - -#: remote_control_password "" *-colors - -#: Finally, the path to a python module can be specified that provides -#: a function is_cmd_allowed that is used to check every remote -#: control command. See rc_custom_auth -#: -#: for details. For example:: - -#: remote_control_password "my passphrase" my_rc_command_checker.py - -#: Relative paths are resolved from the kitty configuration directory. - -# allow_remote_control no - -#: Allow other programs to control kitty. If you turn this on, other -#: programs can control all aspects of kitty, including sending text -#: to kitty windows, opening new windows, closing windows, reading the -#: content of windows, etc. Note that this even works over SSH -#: connections. The default setting of no prevents any form of remote -#: control. The meaning of the various values are: - -#: password -#: Remote control requests received over both the TTY device and the socket are -#: confirmed based on passwords, see remote_control_password. - -#: socket-only -#: Remote control requests received over a socket are accepted unconditionally. -#: Requests received over the TTY are denied. See listen_on. - -#: socket -#: Remote control requests received over a socket are accepted unconditionally. -#: Requests received over the TTY are confirmed based on password. - -#: no -#: Remote control is completely disabled. - -#: yes -#: Remote control requests are always accepted. - -# listen_on none - -#: Listen to the specified UNIX socket for remote control connections. -#: Note that this will apply to all kitty instances. It can be -#: overridden by the kitty --listen-on command line option, which also -#: supports listening on a TCP socket. This option accepts only UNIX -#: sockets, such as unix:${TEMP}/mykitty or unix:@mykitty (on Linux). -#: Environment variables are expanded and relative paths are resolved -#: with respect to the temporary directory. If {kitty_pid} is present, -#: then it is replaced by the PID of the kitty process, otherwise the -#: PID of the kitty process is appended to the value, with a hyphen. -#: See the help for kitty --listen-on for more details. Note that this -#: will be ignored unless allow_remote_control is set to either: yes, -#: socket or socket-only. Changing this option by reloading the config -#: is not supported. - -# env - -#: Specify the environment variables to be set in all child processes. -#: Using the name with an equal sign (e.g. env VAR=) will set it to -#: the empty string. Specifying only the name (e.g. env VAR) will -#: remove the variable from the child process' environment. Note that -#: environment variables are expanded recursively, for example:: - -#: env VAR1=a -#: env VAR2=${HOME}/${VAR1}/b - -#: The value of VAR2 will be /a/b. - -# watcher - -#: Path to python file which will be loaded for watchers -#: . Can be -#: specified more than once to load multiple watchers. The watchers -#: will be added to every kitty window. Relative paths are resolved -#: relative to the kitty config directory. Note that reloading the -#: config will only affect windows created after the reload. - -# exe_search_path - -#: Control where kitty finds the programs to run. The default search -#: order is: First search the system wide PATH, then ~/.local/bin and -#: ~/bin. If still not found, the PATH defined in the login shell -#: after sourcing all its startup files is tried. Finally, if present, -#: the PATH specified by the env option is tried. - -#: This option allows you to prepend, append, or remove paths from -#: this search order. It can be specified multiple times for multiple -#: paths. A simple path will be prepended to the search order. A path -#: that starts with the + sign will be append to the search order, -#: after ~/bin above. A path that starts with the - sign will be -#: removed from the entire search order. For example:: - -#: exe_search_path /some/prepended/path -#: exe_search_path +/some/appended/path -#: exe_search_path -/some/excluded/path - -# update_check_interval 24 - -#: The interval to periodically check if an update to kitty is -#: available (in hours). If an update is found, a system notification -#: is displayed informing you of the available update. The default is -#: to check every 24 hours, set to zero to disable. Update checking is -#: only done by the official binary builds. Distro packages or source -#: builds do not do update checking. Changing this option by reloading -#: the config is not supported. - -# startup_session none - -#: Path to a session file to use for all kitty instances. Can be -#: overridden by using the kitty --session command line option for -#: individual instances. See sessions -#: in the kitty -#: documentation for details. Note that relative paths are interpreted -#: with respect to the kitty config directory. Environment variables -#: in the path are expanded. Changing this option by reloading the -#: config is not supported. - -# clipboard_control write-clipboard write-primary read-clipboard-ask read-primary-ask - -#: Allow programs running in kitty to read and write from the -#: clipboard. You can control exactly which actions are allowed. The -#: possible actions are: write-clipboard, read-clipboard, write- -#: primary, read-primary, read-clipboard-ask, read-primary-ask. The -#: default is to allow writing to the clipboard and primary selection -#: and to ask for permission when a program tries to read from the -#: clipboard. Note that disabling the read confirmation is a security -#: risk as it means that any program, even the ones running on a -#: remote server via SSH can read your clipboard. See also -#: clipboard_max_size. - -# clipboard_max_size 512 - -#: The maximum size (in MB) of data from programs running in kitty -#: that will be stored for writing to the system clipboard. A value of -#: zero means no size limit is applied. See also clipboard_control. - -# file_transfer_confirmation_bypass - -#: The password that can be supplied to the file transfer kitten -#: to skip the -#: transfer confirmation prompt. This should only be used when -#: initiating transfers from trusted computers, over trusted networks -#: or encrypted transports, as it allows any programs running on the -#: remote machine to read/write to the local filesystem, without -#: permission. - -# allow_hyperlinks yes - -#: Process hyperlink escape sequences (OSC 8). If disabled OSC 8 -#: escape sequences are ignored. Otherwise they become clickable -#: links, that you can click with the mouse or by using the hints -#: kitten . The -#: special value of ask means that kitty will ask before opening the -#: link when clicked. - -# shell_integration enabled - -#: Enable shell integration on supported shells. This enables features -#: such as jumping to previous prompts, browsing the output of the -#: previous command in a pager, etc. on supported shells. Set to -#: disabled to turn off shell integration, completely. It is also -#: possible to disable individual features, set to a space separated -#: list of these values: no-rc, no-cursor, no-title, no-cwd, no- -#: prompt-mark, no-complete. See Shell integration -#: for details. - -# allow_cloning ask - -#: Control whether programs running in the terminal can request new -#: windows to be created. The canonical example is clone-in-kitty -#: . -#: By default, kitty will ask for permission for each clone request. -#: Allowing cloning unconditionally gives programs running in the -#: terminal (including over SSH) permission to execute arbitrary code, -#: as the user who is running the terminal, on the computer that the -#: terminal is running on. - -# clone_source_strategies venv,conda,env_var,path - -#: Control what shell code is sourced when running clone-in-kitty in -#: the newly cloned window. The supported strategies are: - -#: venv -#: Source the file $VIRTUAL_ENV/bin/activate. This is used by the -#: Python stdlib venv module and allows cloning venvs automatically. -#: conda -#: Run conda activate $CONDA_DEFAULT_ENV. This supports the virtual -#: environments created by conda. -#: env_var -#: Execute the contents of the environment variable -#: KITTY_CLONE_SOURCE_CODE with eval. -#: path -#: Source the file pointed to by the environment variable -#: KITTY_CLONE_SOURCE_PATH. - -#: This option must be a comma separated list of the above values. -#: This only source the first valid one in the above order. - -# term xterm-kitty - -#: The value of the TERM environment variable to set. Changing this -#: can break many terminal programs, only change it if you know what -#: you are doing, not because you read some advice on "Stack Overflow" -#: to change it. The TERM variable is used by various programs to get -#: information about the capabilities and behavior of the terminal. If -#: you change it, depending on what programs you run, and how -#: different the terminal you are changing it to is, various things -#: from key-presses, to colors, to various advanced features may not -#: work. Changing this option by reloading the config will only affect -#: newly created windows. - -#: }}} - -#: OS specific tweaks {{{ - -# wayland_titlebar_color system - -#: The color of the kitty window's titlebar on Wayland systems with -#: client side window decorations such as GNOME. A value of system -#: means to use the default system color, a value of background means -#: to use the background color of the currently active window and -#: finally you can use an arbitrary color, such as #12af59 or red. - -# macos_titlebar_color system - -#: The color of the kitty window's titlebar on macOS. A value of -#: system means to use the default system color, light or dark can -#: also be used to set it explicitly. A value of background means to -#: use the background color of the currently active window and finally -#: you can use an arbitrary color, such as #12af59 or red. WARNING: -#: This option works by using a hack when arbitrary color (or -#: background) is configured, as there is no proper Cocoa API for it. -#: It sets the background color of the entire window and makes the -#: titlebar transparent. As such it is incompatible with -#: background_opacity. If you want to use both, you are probably -#: better off just hiding the titlebar with hide_window_decorations. - -# macos_option_as_alt no - -#: Use the Option key as an Alt key on macOS. With this set to no, -#: kitty will use the macOS native Option+Key to enter Unicode -#: character behavior. This will break any Alt+Key keyboard shortcuts -#: in your terminal programs, but you can use the macOS Unicode input -#: technique. You can use the values: left, right or both to use only -#: the left, right or both Option keys as Alt, instead. Note that -#: kitty itself always treats Option the same as Alt. This means you -#: cannot use this option to configure different kitty shortcuts for -#: Option+Key vs. Alt+Key. Also, any kitty shortcuts using -#: Option/Alt+Key will take priority, so that any such key presses -#: will not be passed to terminal programs running inside kitty. -#: Changing this option by reloading the config is not supported. - -# macos_hide_from_tasks no - -#: Hide the kitty window from running tasks on macOS (⌘+Tab and the -#: Dock). Changing this option by reloading the config is not -#: supported. - -# macos_quit_when_last_window_closed no - -#: Have kitty quit when all the top-level windows are closed on macOS. -#: By default, kitty will stay running, even with no open windows, as -#: is the expected behavior on macOS. - -# macos_window_resizable yes - -#: Disable this if you want kitty top-level OS windows to not be -#: resizable on macOS. Changing this option by reloading the config -#: will only affect newly created OS windows. - -# macos_thicken_font 0 - -#: Draw an extra border around the font with the given width, to -#: increase legibility at small font sizes on macOS. For example, a -#: value of 0.75 will result in rendering that looks similar to sub- -#: pixel antialiasing at common font sizes. - -# macos_traditional_fullscreen no - -#: Use the macOS traditional full-screen transition, that is faster, -#: but less pretty. - -# macos_show_window_title_in all - -#: Control where the window title is displayed on macOS. A value of -#: window will show the title of the currently active window at the -#: top of the macOS window. A value of menubar will show the title of -#: the currently active window in the macOS global menu bar, making -#: use of otherwise wasted space. A value of all will show the title -#: in both places, and none hides the title. See -#: macos_menubar_title_max_length for how to control the length of the -#: title in the menu bar. - -# macos_menubar_title_max_length 0 - -#: The maximum number of characters from the window title to show in -#: the macOS global menu bar. Values less than one means that there is -#: no maximum limit. - -# macos_custom_beam_cursor no - -#: Use a custom mouse cursor for macOS that is easier to see on both -#: light and dark backgrounds. Nowadays, the default macOS cursor -#: already comes with a white border. WARNING: this might make your -#: mouse cursor invisible on dual GPU machines. Changing this option -#: by reloading the config is not supported. - -# macos_colorspace srgb - -#: The colorspace in which to interpret terminal colors. The default -#: of srgb will cause colors to match those seen in web browsers. The -#: value of default will use whatever the native colorspace of the -#: display is. The value of displayp3 will use Apple's special -#: snowflake display P3 color space, which will result in over -#: saturated (brighter) colors with some color shift. Reloading -#: configuration will change this value only for newly created OS -#: windows. - -# linux_display_server auto - -#: Choose between Wayland and X11 backends. By default, an appropriate -#: backend based on the system state is chosen automatically. Set it -#: to x11 or wayland to force the choice. Changing this option by -#: reloading the config is not supported. - -#: }}} - -#: Keyboard shortcuts {{{ - -#: Keys are identified simply by their lowercase Unicode characters. -#: For example: a for the A key, [ for the left square bracket key, -#: etc. For functional keys, such as Enter or Escape, the names are -#: present at Functional key definitions -#: . -#: For modifier keys, the names are ctrl (control, ⌃), shift (⇧), alt -#: (opt, option, ⌥), super (cmd, command, ⌘). See also: GLFW mods -#: - -#: On Linux you can also use XKB key names to bind keys that are not -#: supported by GLFW. See XKB keys -#: for a list of key names. The name to use is the part -#: after the XKB_KEY_ prefix. Note that you can only use an XKB key -#: name for keys that are not known as GLFW keys. - -#: Finally, you can use raw system key codes to map keys, again only -#: for keys that are not known as GLFW keys. To see the system key -#: code for a key, start kitty with the kitty --debug-input option, -#: kitty will output some debug text for every key event. In that text -#: look for native_code, the value of that becomes the key name in the -#: shortcut. For example: - -#: .. code-block:: none - -#: on_key_input: glfw key: 0x61 native_code: 0x61 action: PRESS mods: none text: 'a' - -#: Here, the key name for the A key is 0x61 and you can use it with:: - -#: map ctrl+0x61 something - -#: to map Ctrl+A to something. - -#: You can use the special action no_op to unmap a keyboard shortcut -#: that is assigned in the default configuration:: - -#: map kitty_mod+space no_op - -#: If you would like kitty to completely ignore a key event, not even -#: sending it to the program running in the terminal, map it to -#: discard_event:: - -#: map kitty_mod+f1 discard_event - -#: You can combine multiple actions to be triggered by a single -#: shortcut with combine action, using the syntax below:: - -#: map key combine action1 action2 action3 ... - -#: For example:: - -#: map kitty_mod+e combine : new_window : next_layout - -#: This will create a new window and switch to the next available -#: layout. - -#: You can use multi-key shortcuts with the syntax shown below:: - -#: map key1>key2>key3 action - -#: For example:: - -#: map ctrl+f>2 set_font_size 20 - -#: The full list of actions that can be mapped to key presses is -#: available here . - -# kitty_mod ctrl+shift - -#: Special modifier key alias for default shortcuts. You can change -#: the value of this option to alter all default shortcuts that use -#: kitty_mod. - -# clear_all_shortcuts no - -#: Remove all shortcut definitions up to this point. Useful, for -#: instance, to remove the default shortcuts. - -# action_alias - -#: E.g. action_alias launch_tab launch --type=tab --cwd=current - -#: Define action aliases to avoid repeating the same options in -#: multiple mappings. Aliases can be defined for any action and will -#: be expanded recursively. For example, the above alias allows you to -#: create mappings to launch a new tab in the current working -#: directory without duplication:: - -#: map f1 launch_tab vim -#: map f2 launch_tab emacs - -#: Similarly, to alias kitten invocation:: - -#: action_alias hints kitten hints --hints-offset=0 - -# kitten_alias - -#: E.g. kitten_alias hints hints --hints-offset=0 - -#: Like action_alias above, but specifically for kittens. Generally, -#: prefer to use action_alias. This option is a legacy version, -#: present for backwards compatibility. It causes all invocations of -#: the aliased kitten to be substituted. So the example above will -#: cause all invocations of the hints kitten to have the --hints- -#: offset=0 option applied. - -#: Clipboard {{{ - -#: Copy to clipboard - -map kitty_mod+c copy_to_clipboard -# map cmd+c copy_to_clipboard - -#:: There is also a copy_or_interrupt action that can be optionally -#:: mapped to Ctrl+C. It will copy only if there is a selection and -#:: send an interrupt otherwise. Similarly, -#:: copy_and_clear_or_interrupt will copy and clear the selection or -#:: send an interrupt if there is no selection. - -#: Paste from clipboard - -map kitty_mod+v paste_from_clipboard -# map cmd+v paste_from_clipboard - -#: Paste from selection - -# map kitty_mod+s paste_from_selection -# map shift+insert paste_from_selection - -#: Pass selection to program - -# map kitty_mod+o pass_selection_to_program - -#:: You can also pass the contents of the current selection to any -#:: program with pass_selection_to_program. By default, the system's -#:: open program is used, but you can specify your own, the selection -#:: will be passed as a command line argument to the program. For -#:: example:: - -#:: map kitty_mod+o pass_selection_to_program firefox - -#:: You can pass the current selection to a terminal program running -#:: in a new kitty window, by using the @selection placeholder:: - -#:: map kitty_mod+y new_window less @selection - -#: }}} - -#: Scrolling {{{ - -#: Scroll line up - -# map kitty_mod+up scroll_line_up -# map kitty_mod+k scroll_line_up -# map opt+cmd+page_up scroll_line_up -# map cmd+up scroll_line_up - -#: Scroll line down - -# map kitty_mod+down scroll_line_down -# map kitty_mod+j scroll_line_down -# map opt+cmd+page_down scroll_line_down -# map cmd+down scroll_line_down - -#: Scroll page up - -# map kitty_mod+page_up scroll_page_up -# map cmd+page_up scroll_page_up - -#: Scroll page down - -# map kitty_mod+page_down scroll_page_down -# map cmd+page_down scroll_page_down - -#: Scroll to top - -# map kitty_mod+home scroll_home -# map cmd+home scroll_home - -#: Scroll to bottom - -# map kitty_mod+end scroll_end -# map cmd+end scroll_end - -#: Scroll to previous shell prompt - -# map kitty_mod+z scroll_to_prompt -1 - -#:: Use a parameter of 0 for scroll_to_prompt to scroll to the last -#:: jumped to or the last clicked position. Requires shell -#:: integration -#:: to work. - -#: Scroll to next shell prompt - -# map kitty_mod+x scroll_to_prompt 1 - -#: Browse scrollback buffer in pager - -# map kitty_mod+h show_scrollback - -#:: You can pipe the contents of the current screen and history -#:: buffer as STDIN to an arbitrary program using launch --stdin- -#:: source. For example, the following opens the scrollback buffer in -#:: less in an overlay window:: - -#:: map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R - -#:: For more details on piping screen and buffer contents to external -#:: programs, see launch . - -#: Browse output of the last shell command in pager - -# map kitty_mod+g show_last_command_output - -#:: You can also define additional shortcuts to get the command -#:: output. For example, to get the first command output on screen:: - -#:: map f1 show_first_command_output_on_screen - -#:: To get the command output that was last accessed by a keyboard -#:: action or mouse action:: - -#:: map f1 show_last_visited_command_output - -#:: You can pipe the output of the last command run in the shell -#:: using the launch action. For example, the following opens the -#:: output in less in an overlay window:: - -#:: map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R - -#:: To get the output of the first command on the screen, use -#:: @first_cmd_output_on_screen. To get the output of the last jumped -#:: to command, use @last_visited_cmd_output. - -#:: Requires shell integration -#:: to work. - -#: }}} - -#: Window management {{{ - -#: New window - -map kitty_mod+enter launch --cwd=current -# map cmd+enter new_window - -#:: You can open a new kitty window running an arbitrary program, for -#:: example:: - -#:: map kitty_mod+y launch mutt - -#:: You can open a new window with the current working directory set -#:: to the working directory of the current window using:: - -#:: map ctrl+alt+enter launch --cwd=current - -#:: You can open a new window that is allowed to control kitty via -#:: the kitty remote control facility with launch --allow-remote- -#:: control. Any programs running in that window will be allowed to -#:: control kitty. For example:: - -#:: map ctrl+enter launch --allow-remote-control some_program - -#:: You can open a new window next to the currently active window or -#:: as the first window, with:: - -#:: map ctrl+n launch --location=neighbor -#:: map ctrl+f launch --location=first - -#:: For more details, see launch -#:: . - -#: New OS window - -# map kitty_mod+n new_os_window -# map cmd+n new_os_window - -#:: Works like new_window above, except that it opens a top-level OS -#:: window. In particular you can use new_os_window_with_cwd to open -#:: a window with the current working directory. - -#: Close window - -# map kitty_mod+w close_window -# map shift+cmd+d close_window - -#: Next window - -# map kitty_mod+] next_window - -#: Previous window - -# map kitty_mod+[ previous_window - -#: Move window forward - -# map kitty_mod+f move_window_forward - -#: Move window backward - -# map kitty_mod+b move_window_backward - -#: Move window to top - -# map kitty_mod+` move_window_to_top - -#: Start resizing window - -# map kitty_mod+r start_resizing_window -# map cmd+r start_resizing_window - -#: First window - -# map kitty_mod+1 first_window -# map cmd+1 first_window - -#: Second window - -# map kitty_mod+2 second_window -# map cmd+2 second_window - -#: Third window - -# map kitty_mod+3 third_window -# map cmd+3 third_window - -#: Fourth window - -# map kitty_mod+4 fourth_window -# map cmd+4 fourth_window - -#: Fifth window - -# map kitty_mod+5 fifth_window -# map cmd+5 fifth_window - -#: Sixth window - -# map kitty_mod+6 sixth_window -# map cmd+6 sixth_window - -#: Seventh window - -# map kitty_mod+7 seventh_window -# map cmd+7 seventh_window - -#: Eight window - -# map kitty_mod+8 eighth_window -# map cmd+8 eighth_window - -#: Ninth window - -# map kitty_mod+9 ninth_window -# map cmd+9 ninth_window - -#: Tenth window - -# map kitty_mod+0 tenth_window - -#: Visually select and focus window - -# map kitty_mod+f7 focus_visible_window - -#:: Display overlay numbers and alphabets on the window, and switch -#:: the focus to the window when you press the key. When there are -#:: only two windows, the focus will be switched directly without -#:: displaying the overlay. You can change the overlay characters and -#:: their order with option visual_window_select_characters. - -#: Visually swap window with another - -# map kitty_mod+f8 swap_with_window - -#:: Works like focus_visible_window above, but swaps the window. - -#: }}} - -#: Tab management {{{ - -#: Next tab - -# map kitty_mod+right next_tab -# map shift+cmd+] next_tab -# map ctrl+tab next_tab - -#: Previous tab - -# map kitty_mod+left previous_tab -# map shift+cmd+[ previous_tab -# map ctrl+shift+tab previous_tab - -#: New tab - -# map kitty_mod+t new_tab -map kitty_mod+t new_tab_with_cwd - -#: Close tab - -# map kitty_mod+q close_tab -# map cmd+w close_tab - -#: Close OS window - -# map shift+cmd+w close_os_window - -#: Move tab forward - -# map kitty_mod+. move_tab_forward - -#: Move tab backward - -# map kitty_mod+, move_tab_backward - -#: Set tab title - -# map kitty_mod+alt+t set_tab_title -# map shift+cmd+i set_tab_title - - -#: You can also create shortcuts to go to specific tabs, with 1 being -#: the first tab, 2 the second tab and -1 being the previously active -#: tab, and any number larger than the last tab being the last tab:: - -#: map ctrl+alt+1 goto_tab 1 -#: map ctrl+alt+2 goto_tab 2 - -#: Just as with new_window above, you can also pass the name of -#: arbitrary commands to run when using new_tab and new_tab_with_cwd. -#: Finally, if you want the new tab to open next to the current tab -#: rather than at the end of the tabs list, use:: - -# map ctrl+t new_tab_with_cwd -#: }}} - -#: Layout management {{{ - -#: Next layout - -# map kitty_mod+l next_layout - - -#: You can also create shortcuts to switch to specific layouts:: - -#: map ctrl+alt+t goto_layout tall -#: map ctrl+alt+s goto_layout stack - -#: Similarly, to switch back to the previous layout:: - -#: map ctrl+alt+p last_used_layout - -#: There is also a toggle_layout action that switches to the named -#: layout or back to the previous layout if in the named layout. -#: Useful to temporarily "zoom" the active window by switching to the -#: stack layout:: - -#: map ctrl+alt+z toggle_layout stack -#: }}} - -#: Font sizes {{{ - -#: You can change the font size for all top-level kitty OS windows at -#: a time or only the current one. - -#: Increase font size - -# map kitty_mod+equal change_font_size all +2.0 -# map kitty_mod+plus change_font_size all +2.0 -# map kitty_mod+kp_add change_font_size all +2.0 -# map cmd+plus change_font_size all +2.0 -# map cmd+equal change_font_size all +2.0 -# map shift+cmd+equal change_font_size all +2.0 - -#: Decrease font size - -# map kitty_mod+minus change_font_size all -2.0 -# map kitty_mod+kp_subtract change_font_size all -2.0 -# map cmd+minus change_font_size all -2.0 -# map shift+cmd+minus change_font_size all -2.0 - -#: Reset font size - -# map kitty_mod+backspace change_font_size all 0 -# map cmd+0 change_font_size all 0 - - -#: To setup shortcuts for specific font sizes:: - -#: map kitty_mod+f6 change_font_size all 10.0 - -#: To setup shortcuts to change only the current OS window's font -#: size:: - -#: map kitty_mod+f6 change_font_size current 10.0 -#: }}} - -#: Select and act on visible text {{{ - -#: Use the hints kitten to select text and either pass it to an -#: external program or insert it into the terminal or copy it to the -#: clipboard. - -#: Open URL - -# map kitty_mod+e open_url_with_hints - -#:: Open a currently visible URL using the keyboard. The program used -#:: to open the URL is specified in open_url_with. - -#: Insert selected path - -# map kitty_mod+p>f kitten hints --type path --program - - -#:: Select a path/filename and insert it into the terminal. Useful, -#:: for instance to run git commands on a filename output from a -#:: previous git command. - -#: Open selected path - -# map kitty_mod+p>shift+f kitten hints --type path - -#:: Select a path/filename and open it with the default open program. - -#: Insert selected line - -# map kitty_mod+p>l kitten hints --type line --program - - -#:: Select a line of text and insert it into the terminal. Useful for -#:: the output of things like: `ls -1`. - -#: Insert selected word - -# map kitty_mod+p>w kitten hints --type word --program - - -#:: Select words and insert into terminal. - -#: Insert selected hash - -# map kitty_mod+p>h kitten hints --type hash --program - - -#:: Select something that looks like a hash and insert it into the -#:: terminal. Useful with git, which uses SHA1 hashes to identify -#:: commits. - -#: Open the selected file at the selected line - -# map kitty_mod+p>n kitten hints --type linenum - -#:: Select something that looks like filename:linenum and open it in -#:: vim at the specified line number. - -#: Open the selected hyperlink - -# map kitty_mod+p>y kitten hints --type hyperlink - -#:: Select a hyperlink (i.e. a URL that has been marked as such by -#:: the terminal program, for example, by `ls --hyperlink=auto`). - - -#: The hints kitten has many more modes of operation that you can map -#: to different shortcuts. For a full description see hints kitten -#: . -#: }}} - -#: Miscellaneous {{{ - -#: Show documentation - -# map kitty_mod+f1 show_kitty_doc overview - -#: Toggle fullscreen - -# map kitty_mod+f11 toggle_fullscreen -# map ctrl+cmd+f toggle_fullscreen - -#: Toggle maximized - -# map kitty_mod+f10 toggle_maximized - -#: Toggle macOS secure keyboard entry - -# map opt+cmd+s toggle_macos_secure_keyboard_entry - -#: Unicode input - -# map kitty_mod+u kitten unicode_input -# map ctrl+cmd+space kitten unicode_input - -#: Edit config file - -# map kitty_mod+f2 edit_config_file -# map cmd+, edit_config_file - -#: Open the kitty command shell - -# map kitty_mod+escape kitty_shell window - -#:: Open the kitty shell in a new window / tab / overlay / os_window -#:: to control kitty using commands. - -#: Increase background opacity - -# map kitty_mod+a>m set_background_opacity +0.1 - -#: Decrease background opacity - -# map kitty_mod+a>l set_background_opacity -0.1 - -#: Make background fully opaque - -# map kitty_mod+a>1 set_background_opacity 1 - -#: Reset background opacity - -# map kitty_mod+a>d set_background_opacity default - -#: Reset the terminal - -# map kitty_mod+delete clear_terminal reset active -# map opt+cmd+r clear_terminal reset active - -#:: You can create shortcuts to clear/reset the terminal. For -#:: example:: - -#:: # Reset the terminal -#:: map f1 clear_terminal reset active -#:: # Clear the terminal screen by erasing all contents -#:: map f1 clear_terminal clear active -#:: # Clear the terminal scrollback by erasing it -#:: map f1 clear_terminal scrollback active -#:: # Scroll the contents of the screen into the scrollback -#:: map f1 clear_terminal scroll active -#:: # Clear everything up to the line with the cursor -#:: map f1 clear_terminal to_cursor active - -#:: If you want to operate on all kitty windows instead of just the -#:: current one, use all instead of active. - -#:: It is also possible to remap Ctrl+L to both scroll the current -#:: screen contents into the scrollback buffer and clear the screen, -#:: instead of just clearing the screen, for example, for ZSH add the -#:: following to ~/.zshrc: - -#:: .. code-block:: zsh - -#:: scroll-and-clear-screen() { -#:: printf '\n%.0s' {1..$LINES} -#:: zle clear-screen -#:: } -#:: zle -N scroll-and-clear-screen -#:: bindkey '^l' scroll-and-clear-screen - -#: Clear up to cursor line - -# map cmd+k clear_terminal to_cursor active - -#: Reload kitty.conf - -# map kitty_mod+f5 load_config_file -# map ctrl+cmd+, load_config_file - -#:: Reload kitty.conf, applying any changes since the last time it -#:: was loaded. Note that a handful of options cannot be dynamically -#:: changed and require a full restart of kitty. Particularly, when -#:: changing shortcuts for actions located on the macOS global menu -#:: bar, a full restart is needed. You can also map a keybinding to -#:: load a different config file, for example:: - -#:: map f5 load_config /path/to/alternative/kitty.conf - -#:: Note that all options from the original kitty.conf are discarded, -#:: in other words the new configuration *replace* the old ones. - -#: Debug kitty configuration - -# map kitty_mod+f6 debug_config -# map opt+cmd+, debug_config - -#:: Show details about exactly what configuration kitty is running -#:: with and its host environment. Useful for debugging issues. - -#: Send arbitrary text on key presses - -#:: E.g. map ctrl+shift+alt+h send_text all Hello World - -#:: You can tell kitty to send arbitrary (UTF-8) encoded text to the -#:: client program when pressing specified shortcut keys. For -#:: example:: - -#:: map ctrl+alt+a send_text all Special text - -#:: This will send "Special text" when you press the Ctrl+Alt+A key -#:: combination. The text to be sent decodes ANSI C escapes -#:: so you can use escapes like \e to send control -#:: codes or \u21fb to send Unicode characters (or you can just input -#:: the Unicode characters directly as UTF-8 text). You can use -#:: `kitty +kitten show_key` to get the key escape codes you want to -#:: emulate. - -#:: The first argument to send_text is the keyboard modes in which to -#:: activate the shortcut. The possible values are normal, -#:: application, kitty or a comma separated combination of them. The -#:: modes normal and application refer to the DECCKM cursor key mode -#:: for terminals, and kitty refers to the kitty extended keyboard -#:: protocol. The special value all means all of them. - -#:: Some more examples:: - -#:: # Output a word and move the cursor to the start of the line (like typing and pressing Home) -#:: map ctrl+alt+a send_text normal Word\e[H -#:: map ctrl+alt+a send_text application Word\eOH -#:: # Run a command at a shell prompt (like typing the command and pressing Enter) -#:: map ctrl+alt+a send_text normal,application some command with arguments\r - -#: Open kitty Website - -# map shift+cmd+/ open_url https://sw.kovidgoyal.net/kitty/ - -#: Hide macOS kitty application - -# map cmd+h hide_macos_app - -#: Hide macOS other applications - -# map opt+cmd+h hide_macos_other_apps - -#: Minimize macOS window - -# map cmd+m minimize_macos_window - -#: Quit kitty - -# map cmd+q quit - -#: }}} - -#: }}} -# -# map ctrl+f launch --type=overlay --stdin-source=@screen_scrollback /bin/sh -c "/usr/local/bin/fzf --no-sort --no-mouse --exact -i --tac | kitty +kitten clipboard" - -map cmd+/ launch --allow-remote-control kitty +kitten kitty_search/search.py @active-kitty-window-id - -# Add or uncomment the following line to enable shell integration: - -enable_kitty_shell_integration source ~/.zshrc - -# BEGIN_KITTY_THEME -# Tomorrow Night Bright -include current-theme.conf -# END_KITTY_THEME diff --git a/kitty/kitty.conf b/kitty/kitty.conf new file mode 120000 index 0000000..37d2a5c --- /dev/null +++ b/kitty/kitty.conf @@ -0,0 +1 @@ +/Users/jon/.config/kitty/kitty.conf \ No newline at end of file From 8fd5ef607ae467c71eaa48f8b208c3cb9a05d57f Mon Sep 17 00:00:00 2001 From: Jonathan Date: Thu, 18 Jul 2024 13:14:41 -0700 Subject: [PATCH 25/25] Update settings for vim and osx --- antigen/init.zsh | 98 ------------------------------------------------ macprefs | 3 ++ vim/.vimrc | 1 + 3 files changed, 4 insertions(+), 98 deletions(-) delete mode 100644 antigen/init.zsh create mode 120000 vim/.vimrc diff --git a/antigen/init.zsh b/antigen/init.zsh deleted file mode 100644 index 5d125e3..0000000 --- a/antigen/init.zsh +++ /dev/null @@ -1,98 +0,0 @@ -#-- START ZCACHE GENERATED FILE -#-- GENERATED: Fri Mar 10 00:14:02 PST 2023 -#-- ANTIGEN v2.2.2 -_antigen () { - local -a _1st_arguments - _1st_arguments=('apply:Load all bundle completions' 'bundle:Install and load the given plugin' 'bundles:Bulk define bundles' 'cleanup:Clean up the clones of repos which are not used by any bundles currently loaded' 'cache-gen:Generate cache' 'init:Load Antigen configuration from file' 'list:List out the currently loaded bundles' 'purge:Remove a cloned bundle from filesystem' 'reset:Clears cache' 'restore:Restore the bundles state as specified in the snapshot' 'revert:Revert the state of all bundles to how they were before the last antigen update' 'selfupdate:Update antigen itself' 'snapshot:Create a snapshot of all the active clones' 'theme:Switch the prompt theme' 'update:Update all bundles' 'use:Load any (supported) zsh pre-packaged framework') - _1st_arguments+=('help:Show this message' 'version:Display Antigen version') - __bundle () { - _arguments '--loc[Path to the location ]' '--url[Path to the repository ]' '--branch[Git branch name]' '--no-local-clone[Do not create a clone]' - } - __list () { - _arguments '--simple[Show only bundle name]' '--short[Show only bundle name and branch]' '--long[Show bundle records]' - } - __cleanup () { - _arguments '--force[Do not ask for confirmation]' - } - _arguments '*:: :->command' - if (( CURRENT == 1 )) - then - _describe -t commands "antigen command" _1st_arguments - return - fi - local -a _command_args - case "$words[1]" in - (bundle) __bundle ;; - (use) compadd "$@" "oh-my-zsh" "prezto" ;; - (cleanup) __cleanup ;; - (update|purge) compadd $(type -f \-antigen-get-bundles &> /dev/null || antigen &> /dev/null; -antigen-get-bundles --simple 2> /dev/null) ;; - (theme) compadd $(type -f \-antigen-get-themes &> /dev/null || antigen &> /dev/null; -antigen-get-themes 2> /dev/null) ;; - (list) __list ;; - esac -} -antigen () { - local MATCH MBEGIN MEND - [[ "$ZSH_EVAL_CONTEXT" =~ "toplevel:*" || "$ZSH_EVAL_CONTEXT" =~ "cmdarg:*" ]] && source "/Users/jon/antigen.zsh" && eval antigen $@; - return 0; -} -typeset -gaU fpath path -fpath+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) path+=(/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos /Users/jon/.antigen/bundles/therealklanni/purity /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm /Users/jon/.antigen/bundles/lukechilds/zsh-nvm /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn /Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder /Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases /Users/jon/.antigen/bundles/webyneter/docker-aliases /Users/jon/.antigen/bundles/zsh-users/zsh-completions /Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions /Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting) -_antigen_compinit () { - autoload -Uz compinit; compinit -d "/Users/jon/.antigen/.zcompdump"; compdef _antigen antigen - add-zsh-hook -D precmd _antigen_compinit -} -autoload -Uz add-zsh-hook; add-zsh-hook precmd _antigen_compinit -compdef () {} - -if [[ -n "/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh" ]]; then - ZSH="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh"; ZSH_CACHE_DIR="/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/cache/" -fi -#--- BUNDLES BEGIN -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh'; -source '/Users/jon/.antigen/bundles/therealklanni/purity/purity.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh'; -source '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm/zsh-nvm.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh'; -source '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh'; -source '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases/zsh-docker-compose-aliases.zsh'; -source '/Users/jon/.antigen/bundles/webyneter/docker-aliases/docker-aliases.plugin.zsh'; -source '/Users/jon/.antigen/bundles/zsh-users/zsh-completions/zsh-completions.plugin.zsh'; -source '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh'; -source '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh'; - -#--- BUNDLES END -typeset -gaU _ANTIGEN_BUNDLE_RECORD; _ANTIGEN_BUNDLE_RECORD=('https://github.com/robbyrussell/oh-my-zsh.git lib plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/macos plugin true' 'https://github.com/therealklanni/purity.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/git plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/command-not-found plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/docker plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/heroku plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/pip plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/kubernetes plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/lein plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/fzf plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/httpie plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/mosh plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/npm plugin true' 'https://github.com/lukechilds/zsh-nvm.git / plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copyfile plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/copypath plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/yarn plugin true' 'https://github.com/robbyrussell/oh-my-zsh.git plugins/alias-finder plugin true' 'https://github.com/luismayta/zsh-docker-compose-aliases.git / plugin true' 'https://github.com/webyneter/docker-aliases.git / plugin true' 'https://github.com/zsh-users/zsh-completions.git / plugin true' 'https://github.com/zsh-users/zsh-autosuggestions.git / plugin true' 'https://github.com/zsh-users/zsh-syntax-highlighting.git / plugin true') -typeset -g _ANTIGEN_CACHE_LOADED; _ANTIGEN_CACHE_LOADED=true -typeset -ga _ZCACHE_BUNDLE_SOURCE; _ZCACHE_BUNDLE_SOURCE=('/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/bzr.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/cli.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/clipboard.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/compfix.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/completion.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/correction.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/diagnostics.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/directories.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/git.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/grep.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/history.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/key-bindings.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/misc.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/nvm.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/prompt_info_functions.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/spectrum.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/termsupport.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/theme-and-appearance.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/lib/vcs_info.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/macos/macos.plugin.zsh' '/Users/jon/.antigen/bundles/therealklanni/purity//' '/Users/jon/.antigen/bundles/therealklanni/purity///purity.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/git/git.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/command-not-found/command-not-found.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/docker' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/heroku/heroku.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/pip/pip.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/kubernetes' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/lein' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/fzf/fzf.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/httpie/httpie.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/mosh/mosh.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/npm/npm.plugin.zsh' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm//' '/Users/jon/.antigen/bundles/lukechilds/zsh-nvm///zsh-nvm.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copyfile/copyfile.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/copypath/copypath.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/yarn/yarn.plugin.zsh' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder' '/Users/jon/.antigen/bundles/robbyrussell/oh-my-zsh/plugins/alias-finder/alias-finder.plugin.zsh' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases//' '/Users/jon/.antigen/bundles/luismayta/zsh-docker-compose-aliases///zsh-docker-compose-aliases.zsh' '/Users/jon/.antigen/bundles/webyneter/docker-aliases//' '/Users/jon/.antigen/bundles/webyneter/docker-aliases///docker-aliases.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-completions///zsh-completions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions//' '/Users/jon/.antigen/bundles/zsh-users/zsh-autosuggestions///zsh-autosuggestions.plugin.zsh' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting//' '/Users/jon/.antigen/bundles/zsh-users/zsh-syntax-highlighting///zsh-syntax-highlighting.plugin.zsh') -typeset -g _ANTIGEN_CACHE_VERSION; _ANTIGEN_CACHE_VERSION='v2.2.2' - -#-- END ZCACHE GENERATED FILE diff --git a/macprefs b/macprefs index 60255f8..440174f 100644 --- a/macprefs +++ b/macprefs @@ -26,3 +26,6 @@ defaults write com.apple.dock autohide-delay -float 0 // Apply the changes immediately killall Dock + +// Show hidden files in finder +defaults write com.apple.Finder AppleShowAllFiles true \ No newline at end of file diff --git a/vim/.vimrc b/vim/.vimrc new file mode 120000 index 0000000..b0a1b2c --- /dev/null +++ b/vim/.vimrc @@ -0,0 +1 @@ +/Users/jon/.vimrc \ No newline at end of file