From 21a570e1736ca12d25526147f8a820df29453d8f Mon Sep 17 00:00:00 2001 From: Hektor Misplon Date: Sun, 16 Feb 2020 08:45:55 +0000 Subject: [PATCH 001/893] Add vim dotfiles --- .vim/autoload/plug.vim | 2597 +++++++++++++++++++++++++++++++ .vim/coc-settings.json | 5 + .vim/plugged/coc.nvim | 1 + .vim/plugged/fzf | 1 + .vim/plugged/fzf.vim | 1 + .vim/plugged/nerdtree | 1 + .vim/plugged/nord-vim | 1 + .vim/plugged/typescript-vim | 1 + .vim/plugged/vim-devicons | 1 + .vim/plugged/vim-jsx-pretty | 1 + .vim/plugged/vim-jsx-typescript | 1 + .vimrc | 39 + 12 files changed, 2650 insertions(+) create mode 100644 .vim/autoload/plug.vim create mode 100644 .vim/coc-settings.json create mode 160000 .vim/plugged/coc.nvim create mode 160000 .vim/plugged/fzf create mode 160000 .vim/plugged/fzf.vim create mode 160000 .vim/plugged/nerdtree create mode 160000 .vim/plugged/nord-vim create mode 160000 .vim/plugged/typescript-vim create mode 160000 .vim/plugged/vim-devicons create mode 160000 .vim/plugged/vim-jsx-pretty create mode 160000 .vim/plugged/vim-jsx-typescript create mode 100644 .vimrc diff --git a/.vim/autoload/plug.vim b/.vim/autoload/plug.vim new file mode 100644 index 0000000..ac14332 --- /dev/null +++ b/.vim/autoload/plug.vim @@ -0,0 +1,2597 @@ +" vim-plug: Vim plugin manager +" ============================ +" +" Download plug.vim and put it in ~/.vim/autoload +" +" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ +" https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim +" +" Edit your .vimrc +" +" call plug#begin('~/.vim/plugged') +" +" " Make sure you use single quotes +" +" " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align +" Plug 'junegunn/vim-easy-align' +" +" " Any valid git URL is allowed +" Plug 'https://github.com/junegunn/vim-github-dashboard.git' +" +" " Multiple Plug commands can be written in a single line using | separators +" Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' +" +" " On-demand loading +" Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } +" Plug 'tpope/vim-fireplace', { 'for': 'clojure' } +" +" " Using a non-master branch +" Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } +" +" " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) +" Plug 'fatih/vim-go', { 'tag': '*' } +" +" " Plugin options +" Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } +" +" " Plugin outside ~/.vim/plugged with post-update hook +" Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } +" +" " Unmanaged plugin (manually installed and updated) +" Plug '~/my-prototype-plugin' +" +" " Initialize plugin system +" call plug#end() +" +" Then reload .vimrc and :PlugInstall to install plugins. +" +" Plug options: +" +"| Option | Description | +"| ----------------------- | ------------------------------------------------ | +"| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | +"| `rtp` | Subdirectory that contains Vim plugin | +"| `dir` | Custom directory for the plugin | +"| `as` | Use different name for the plugin | +"| `do` | Post-update hook (string or funcref) | +"| `on` | On-demand loading: Commands or ``-mappings | +"| `for` | On-demand loading: File types | +"| `frozen` | Do not update unless explicitly specified | +" +" More information: https://github.com/junegunn/vim-plug +" +" +" Copyright (c) 2017 Junegunn Choi +" +" MIT License +" +" Permission is hereby granted, free of charge, to any person obtaining +" a copy of this software and associated documentation files (the +" "Software"), to deal in the Software without restriction, including +" without limitation the rights to use, copy, modify, merge, publish, +" distribute, sublicense, and/or sell copies of the Software, and to +" permit persons to whom the Software is furnished to do so, subject to +" the following conditions: +" +" The above copyright notice and this permission notice shall be +" included in all copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if exists('g:loaded_plug') + finish +endif +let g:loaded_plug = 1 + +let s:cpo_save = &cpo +set cpo&vim + +let s:plug_src = 'https://github.com/junegunn/vim-plug.git' +let s:plug_tab = get(s:, 'plug_tab', -1) +let s:plug_buf = get(s:, 'plug_buf', -1) +let s:mac_gui = has('gui_macvim') && has('gui_running') +let s:is_win = has('win32') +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': 'master', 'frozen': 0 } +let s:TYPE = { +\ 'string': type(''), +\ 'list': type([]), +\ 'dict': type({}), +\ 'funcref': type(function('call')) +\ } +let s:loaded = get(s:, 'loaded', {}) +let s:triggers = get(s:, 'triggers', {}) + +if s:is_win + function! s:plug_call(fn, ...) + let shellslash = &shellslash + try + set noshellslash + return call(a:fn, a:000) + finally + let &shellslash = shellslash + endtry + endfunction +else + function! s:plug_call(fn, ...) + return call(a:fn, a:000) + endfunction +endif + +function! s:plug_getcwd() + return s:plug_call('getcwd') +endfunction + +function! s:plug_fnamemodify(fname, mods) + return s:plug_call('fnamemodify', a:fname, a:mods) +endfunction + +function! s:plug_expand(fmt) + return s:plug_call('expand', a:fmt, 1) +endfunction + +function! s:plug_tempname() + return s:plug_call('tempname') +endfunction + +function! plug#begin(...) + if a:0 > 0 + let s:plug_home_org = a:1 + let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) + elseif exists('g:plug_home') + let home = s:path(g:plug_home) + elseif !empty(&rtp) + let home = s:path(split(&rtp, ',')[0]) . '/plugged' + else + return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') + endif + if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp + return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') + endif + + let g:plug_home = home + let g:plugs = {} + let g:plugs_order = [] + let s:triggers = {} + + call s:define_commands() + return 1 +endfunction + +function! s:define_commands() + command! -nargs=+ -bar Plug call plug#() + 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(v:exception) + endtry +endfunction + +function! s:parse_options(arg) + let opts = copy(s:base_spec) + let type = type(a:arg) + if type == s:TYPE.string + let opts.tag = a:arg + elseif type == s:TYPE.dict + call extend(opts, a:arg) + if has_key(opts, 'dir') + let opts.dir = s:dirpath(s:plug_expand(opts.dir)) + endif + else + throw 'Invalid argument type (expected: string or dictionary)' + endif + return opts +endfunction + +function! s:infer_properties(name, repo) + let repo = a:repo + if s:is_local_plug(repo) + return { 'dir': s:dirpath(s:plug_expand(repo)) } + else + if repo =~ ':' + let uri = repo + else + if repo !~ '/' + throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) + endif + let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') + let uri = printf(fmt, repo) + endif + return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } + endif +endfunction + +function! s:install(force, names) + call s:update_impl(0, a:force, a:names) +endfunction + +function! s:update(force, names) + call s:update_impl(1, a:force, a:names) +endfunction + +function! plug#helptags() + if !exists('g:plugs') + return s:err('plug#begin was not called') + endif + for spec in values(g:plugs) + let docd = join([s:rtp(spec), 'doc'], '/') + if isdirectory(docd) + silent! execute 'helptags' s:esc(docd) + endif + endfor + return 1 +endfunction + +function! s:syntax() + syntax clear + syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber + syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX + syn match plugNumber /[0-9]\+[0-9.]*/ contained + syn match plugBracket /[[\]]/ contained + syn match plugX /x/ contained + syn match plugDash /^-/ + syn match plugPlus /^+/ + syn match plugStar /^*/ + syn match plugMessage /\(^- \)\@<=.*/ + syn match plugName /\(^- \)\@<=[^ ]*:/ + syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ + syn match plugTag /(tag: [^)]\+)/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ + syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag + syn match plugEdge /^ \X\+$/ + syn match plugEdge /^ \X*/ contained nextgroup=plugSha + syn match plugSha /[0-9a-f]\{7,9}/ contained + syn match plugRelDate /([^)]*)$/ contained + syn match plugNotLoaded /(not loaded)$/ + syn match plugError /^x.*/ + syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ + syn match plugH2 /^.*:\n-\+$/ + syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean + hi def link plug1 Title + hi def link plug2 Repeat + hi def link plugH2 Type + hi def link plugX Exception + hi def link plugBracket Structure + hi def link plugNumber Number + + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + + hi def link plugMessage Function + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + + hi def link plugError Error + hi def link plugDeleted Ignore + hi def link plugRelDate Comment + hi def link plugEdge PreProc + hi def link plugSha Identifier + hi def link plugTag Constant + + hi def link plugNotLoaded Comment +endfunction + +function! s:lpad(str, len) + return a:str . repeat(' ', a:len - len(a:str)) +endfunction + +function! s:lines(msg) + return split(a:msg, "[\r\n]") +endfunction + +function! s:lastline(msg) + return get(s:lines(a:msg), -1, '') +endfunction + +function! s:new_window() + execute get(g:, 'plug_window', 'vertical topleft new') +endfunction + +function! s:plug_window_exists() + let buflist = tabpagebuflist(s:plug_tab) + return !empty(buflist) && index(buflist, s:plug_buf) >= 0 +endfunction + +function! s:switch_in() + if !s:plug_window_exists() + return 0 + endif + + if winbufnr(0) != s:plug_buf + let s:pos = [tabpagenr(), winnr(), winsaveview()] + execute 'normal!' s:plug_tab.'gt' + let winnr = bufwinnr(s:plug_buf) + execute winnr.'wincmd w' + call add(s:pos, winsaveview()) + else + let s:pos = [winsaveview()] + endif + + setlocal modifiable + return 1 +endfunction + +function! s:switch_out(...) + call winrestview(s:pos[-1]) + setlocal nomodifiable + if a:0 > 0 + execute a:1 + endif + + if len(s:pos) > 1 + execute 'normal!' s:pos[0].'gt' + execute s:pos[1] 'wincmd w' + call winrestview(s:pos[2]) + endif +endfunction + +function! s:finish_bindings() + nnoremap 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 :if b:plug_preview==1pcendifbd + if a:0 == 0 + call s:finish_bindings() + endif + let b:plug_preview = -1 + let s:plug_tab = tabpagenr() + let s:plug_buf = winbufnr(0) + call s:assign_name() + + for k in ['', 'L', 'o', 'X', 'd', 'dd'] + execute 'silent! unmap ' k + endfor + setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell + if exists('+colorcolumn') + setlocal colorcolumn= + endif + setf vim-plug + if exists('g:syntax_on') + call s:syntax() + endif +endfunction + +function! s:assign_name() + " Assign buffer name + let prefix = '[Plugins]' + let name = prefix + let idx = 2 + while bufexists(name) + let name = printf('%s (%s)', prefix, idx) + let idx = idx + 1 + endwhile + silent! execute 'f' fnameescape(name) +endfunction + +function! s:chsh(swap) + let prev = [&shell, &shellcmdflag, &shellredir] + if !s:is_win && a:swap + set shell=sh shellredir=>%s\ 2>&1 + 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 + let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') + call spec.do({ 'name': name, 'status': status, 'force': a:force }) + catch + let error = v:exception + endtry + else + let error = 'Invalid hook type' + endif + call s:switch_in() + call setline(4, empty(error) ? (getline(4) . 'OK') + \ : ('x' . getline(4)[1:] . error)) + if !empty(error) + call add(s:update.errors, name) + call s:regress_bar() + endif + cd - + endif + endfor +endfunction + +function! s:hash_match(a, b) + return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 +endfunction + +function! s:checkout(spec) + let sha = a:spec.commit + let output = s:system('git rev-parse HEAD', a:spec.dir) + if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) + let output = s:system( + \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) + endif + return output +endfunction + +function! s:finish(pull) + let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) + if new_frozen + let s = new_frozen > 1 ? 's' : '' + call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) + endif + call append(3, '- Finishing ... ') | 4 + redraw + call plug#helptags() + call plug#end() + call setline(4, getline(4) . 'Done!') + redraw + let msgs = [] + if !empty(s:update.errors) + call add(msgs, "Press 'R' to retry.") + endif + if a:pull && len(s:update.new) < len(filter(getline(5, '$'), + \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) + call add(msgs, "Press 'D' to see the updated changes.") + endif + echo join(msgs, ' ') + call s:finish_bindings() +endfunction + +function! s:retry() + if empty(s:update.errors) + return + endif + echo + call s:update_impl(s:update.pull, s:update.force, + \ extend(copy(s:update.errors), [s:update.threads])) +endfunction + +function! s:is_managed(name) + return has_key(g:plugs[a:name], 'uri') +endfunction + +function! s:names(...) + return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) +endfunction + +function! s:check_ruby() + silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") + if !exists('g:plug_ruby') + redraw! + return s:warn('echom', 'Warning: Ruby interface is broken') + endif + let ruby_version = split(g:plug_ruby, '\.') + unlet g:plug_ruby + return s:version_requirement(ruby_version, [1, 8, 7]) +endfunction + +function! s:update_impl(pull, force, args) abort + let sync = index(a:args, '--sync') >= 0 || has('vim_starting') + let args = filter(copy(a:args), 'v:val != "--sync"') + let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? + \ remove(args, -1) : get(g:, 'plug_threads', 16) + + let managed = filter(copy(g:plugs), 's:is_managed(v:key)') + let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : + \ filter(managed, 'index(args, v:key) >= 0') + + if empty(todo) + return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) + endif + + if !s:is_win && s:git_version_requirement(2, 3) + let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' + let $GIT_TERMINAL_PROMPT = 0 + for plug in values(todo) + let plug.uri = substitute(plug.uri, + \ '^https://git::@github\.com', 'https://github.com', '') + endfor + endif + + if !isdirectory(g:plug_home) + try + call mkdir(g:plug_home, 'p') + catch + return s:err(printf('Invalid plug directory: %s. '. + \ 'Try to call plug#begin with a valid directory', g:plug_home)) + endtry + endif + + if has('nvim') && !exists('*jobwait') && threads > 1 + call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') + endif + + let use_job = s:nvim || s:vim8 + let python = (has('python') || has('python3')) && !use_job + let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() + + let s:update = { + \ 'start': reltime(), + \ 'all': todo, + \ 'todo': copy(todo), + \ 'errors': [], + \ 'pull': a:pull, + \ 'force': a:force, + \ 'new': {}, + \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, + \ 'bar': '', + \ 'fin': 0 + \ } + + call s:prepare(1) + call append(0, ['', '']) + normal! 2G + silent! redraw + + let s:clone_opt = get(g:, 'plug_shallow', 1) ? + \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' + + if has('win32unix') || has('wsl') + let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input' + endif + + let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' + + " Python version requirement (>= 2.7) + if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 + redir => pyv + silent python import platform; print platform.python_version() + redir END + let python = s:version_requirement( + \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) + endif + + if (python || ruby) && s:update.threads > 1 + try + let imd = &imd + if s:mac_gui + set noimd + endif + if ruby + call s:update_ruby() + else + call s:update_python() + endif + catch + let lines = getline(4, '$') + let printed = {} + silent! 4,$d _ + for line in lines + let name = s:extract_name(line, '.', '') + if empty(name) || !has_key(printed, name) + call append('$', line) + if !empty(name) + let printed[name] = 1 + if line[0] == 'x' && index(s:update.errors, name) < 0 + call add(s:update.errors, name) + end + endif + endif + endfor + finally + let &imd = imd + call s:update_finish() + endtry + else + call s:update_vim() + while use_job && sync + sleep 100m + if s:update.fin + break + endif + endwhile + endif +endfunction + +function! s:log4(name, msg) + call setline(4, printf('- %s (%s)', a:msg, a:name)) + redraw +endfunction + +function! s:update_finish() + if exists('s:git_terminal_prompt') + let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt + endif + if s:switch_in() + call append(3, '- Updating ...') | 4 + for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) + let [pos, _] = s:logpos(name) + if !pos + continue + endif + if has_key(spec, 'commit') + call s:log4(name, 'Checking out '.spec.commit) + let out = s:checkout(spec) + elseif has_key(spec, 'tag') + let tag = spec.tag + if tag =~ '\*' + let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) + if !v:shell_error && !empty(tags) + let tag = tags[0] + call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) + call append(3, '') + endif + endif + call s:log4(name, 'Checking out '.tag) + let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) + else + let branch = get(spec, 'branch', 'master') + call s:log4(name, 'Merging origin/'.s:esc(branch)) + let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' + \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) + endif + if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && + \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) + call s:log4(name, 'Updating submodules. This may take a while.') + let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) + endif + let msg = s:format_message(v:shell_error ? 'x': '-', name, out) + if v:shell_error + call add(s:update.errors, name) + call s:regress_bar() + silent execute pos 'd _' + call append(4, msg) | 4 + elseif !empty(out) + call setline(pos, msg[0]) + endif + redraw + endfor + silent 4 d _ + try + call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) + catch + call s:warn('echom', v:exception) + call s:warn('echo', '') + return + endtry + call s:finish(s:update.pull) + call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') + call s:switch_out('normal! gg') + endif +endfunction + +function! s:job_abort() + if (!s:nvim && !s:vim8) || !exists('s:jobs') + return + endif + + for [name, j] in items(s:jobs) + if s:nvim + silent! call jobstop(j.jobid) + elseif s:vim8 + silent! call job_stop(j.jobid) + endif + if j.new + call s:rm_rf(g:plugs[name].dir) + endif + endfor + let s:jobs = {} +endfunction + +function! s:last_non_empty_line(lines) + let len = len(a:lines) + for idx in range(len) + let line = a:lines[len-idx-1] + if !empty(line) + return line + endif + endfor + return '' +endfunction + +function! s:job_out_cb(self, data) abort + let self = a:self + let data = remove(self.lines, -1) . a:data + let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') + call extend(self.lines, lines) + " To reduce the number of buffer updates + let self.tick = get(self, 'tick', -1) + 1 + if !self.running || self.tick % len(s:jobs) == 0 + let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') + let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) + call s:log(bullet, self.name, result) + endif +endfunction + +function! s:job_exit_cb(self, data) abort + let a:self.running = 0 + let a:self.error = a:data != 0 + call s:reap(a:self.name) + call s:tick() +endfunction + +function! s:job_cb(fn, job, ch, data) + if !s:plug_window_exists() " plug window closed + return s:job_abort() + endif + call call(a:fn, [a:job, a:data]) +endfunction + +function! s:nvim_cb(job_id, data, event) dict abort + return a:event == 'stdout' ? + \ 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 + let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir, 0) : a:cmd + let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] + + if s:nvim + call extend(job, { + \ 'on_stdout': 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 jid = job_start(s:is_win ? join(argv, ' ') : argv, { + \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), + \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), + \ 'out_mode': 'raw' + \}) + if job_status(jid) == 'run' + let job.jobid = jid + else + let job.running = 0 + let job.error = 1 + let job.lines = ['Failed to start job'] + endif + else + let job.lines = s:lines(call('s:system', [cmd])) + let job.error = v:shell_error != 0 + let job.running = 0 + endif +endfunction + +function! s:reap(name) + let job = s:jobs[a:name] + if job.error + call add(s:update.errors, a:name) + elseif get(job, 'new', 0) + let s:update.new[a:name] = 1 + endif + let s:update.bar .= job.error ? 'x' : '=' + + let bullet = job.error ? 'x' : '-' + let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) + call s:log(bullet, a:name, empty(result) ? 'OK' : result) + call s:bar() + + 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 fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' + call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir }) + else + let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } + endif + else + let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } + endif + else + call s:spawn(name, + \ printf('git clone %s %s %s %s 2>&1', + \ has_tag ? '' : s:clone_opt, + \ prog, + \ plug#shellescape(spec.uri, {'script': 0}), + \ plug#shellescape(s:trim(spec.dir), {'script': 0})), { 'new': 1 }) + endif + + if !s:jobs[name].running + call s:reap(name) + endif + if len(s:jobs) >= s:update.threads + break + endif +endwhile +endfunction + +function! s:update_python() +let py_exe = has('python') ? 'python' : 'python3' +execute py_exe "<< EOF" +import datetime +import functools +import os +try: + import queue +except ImportError: + import Queue as queue +import random +import re +import shutil +import signal +import subprocess +import tempfile +import threading as thr +import time +import traceback +import vim + +G_NVIM = vim.eval("has('nvim')") == '1' +G_PULL = vim.eval('s:update.pull') == '1' +G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 +G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) +G_CLONE_OPT = vim.eval('s:clone_opt') +G_PROGRESS = vim.eval('s:progress_opt(1)') +G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) +G_STOP = thr.Event() +G_IS_WIN = vim.eval('s:is_win') == '1' + +class PlugError(Exception): + def __init__(self, msg): + self.msg = msg +class CmdTimedOut(PlugError): + pass +class CmdFailed(PlugError): + pass +class InvalidURI(PlugError): + pass +class Action(object): + INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] + +class Buffer(object): + def __init__(self, lock, num_plugs, is_pull): + self.bar = '' + self.event = 'Updating' if is_pull else 'Installing' + self.lock = lock + self.maxy = int(vim.eval('winheight(".")')) + self.num_plugs = num_plugs + + def __where(self, name): + """ Find first line with name in current buffer. Return line num. """ + found, lnum = False, 0 + matcher = re.compile('^[-+x*] {0}:'.format(name)) + for line in vim.current.buffer: + if matcher.search(line) is not None: + found = True + break + lnum += 1 + + if not found: + lnum = -1 + return lnum + + def header(self): + curbuf = vim.current.buffer + curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) + + num_spaces = self.num_plugs - len(self.bar) + curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') + + with self.lock: + vim.command('normal! 2G') + vim.command('redraw') + + def write(self, action, name, lines): + first, rest = lines[0], lines[1:] + msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] + msg.extend([' ' + line for line in rest]) + + try: + if action == Action.ERROR: + self.bar += 'x' + vim.command("call add(s:update.errors, '{0}')".format(name)) + elif action == Action.DONE: + self.bar += '=' + + curbuf = vim.current.buffer + lnum = self.__where(name) + if lnum != -1: # Found matching line num + del curbuf[lnum] + if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): + lnum = 3 + else: + lnum = 3 + curbuf.append(msg, lnum) + + self.header() + except vim.error: + pass + +class Command(object): + CD = 'cd /d' if G_IS_WIN else 'cd' + + def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): + self.cmd = cmd + if cmd_dir: + self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) + self.timeout = timeout + self.callback = cb if cb else (lambda msg: None) + self.clean = clean if clean else (lambda: None) + self.proc = None + + @property + def alive(self): + """ Returns true only if command still running. """ + return self.proc and self.proc.poll() is None + + def execute(self, ntries=3): + """ Execute the command with ntries if CmdTimedOut. + Returns the output of the command if no Exception. + """ + attempt, finished, limit = 0, False, self.timeout + + while not finished: + try: + attempt += 1 + result = self.try_command() + finished = True + return result + except CmdTimedOut: + if attempt != ntries: + self.notify_retry() + self.timeout += limit + else: + raise + + def notify_retry(self): + """ Retry required for command, notify user. """ + for count in range(3, 0, -1): + if G_STOP.is_set(): + raise KeyboardInterrupt + msg = 'Timeout. Will retry in {0} second{1} ...'.format( + count, 's' if count != 1 else '') + self.callback([msg]) + time.sleep(1) + self.callback(['Retrying ...']) + + def try_command(self): + """ Execute a cmd & poll for callback. Returns list of output. + Raises CmdFailed -> return code for Popen isn't 0 + Raises CmdTimedOut -> command exceeded timeout without new output + """ + first_line = True + + try: + tfile = tempfile.NamedTemporaryFile(mode='w+b') + preexec_fn = not G_IS_WIN and os.setsid or None + self.proc = subprocess.Popen(self.cmd, stdout=tfile, + stderr=subprocess.STDOUT, + stdin=subprocess.PIPE, shell=True, + preexec_fn=preexec_fn) + thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) + thrd.start() + + thread_not_started = True + while thread_not_started: + try: + thrd.join(0.1) + thread_not_started = False + except RuntimeError: + pass + + while self.alive: + if G_STOP.is_set(): + raise KeyboardInterrupt + + if first_line or random.random() < G_LOG_PROB: + first_line = False + line = '' if G_IS_WIN else nonblock_read(tfile.name) + if line: + self.callback([line]) + + time_diff = time.time() - os.path.getmtime(tfile.name) + if time_diff > self.timeout: + raise CmdTimedOut(['Timeout!']) + + thrd.join(0.5) + + tfile.seek(0) + result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] + + if self.proc.returncode != 0: + raise CmdFailed([''] + result) + + return result + except: + self.terminate() + raise + + def terminate(self): + """ Terminate process and cleanup. """ + if self.alive: + if G_IS_WIN: + os.kill(self.proc.pid, signal.SIGINT) + else: + os.killpg(self.proc.pid, signal.SIGTERM) + self.clean() + +class Plugin(object): + def __init__(self, name, args, buf_q, lock): + self.name = name + self.args = args + self.buf_q = buf_q + self.lock = lock + self.tag = args.get('tag', 0) + + def manage(self): + try: + if os.path.exists(self.args['dir']): + self.update() + else: + self.install() + with self.lock: + thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) + except PlugError as exc: + self.write(Action.ERROR, self.name, exc.msg) + except KeyboardInterrupt: + G_STOP.set() + self.write(Action.ERROR, self.name, ['Interrupted!']) + except: + # Any exception except those above print stack trace + msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) + self.write(Action.ERROR, self.name, msg.split('\n')) + raise + + def install(self): + target = self.args['dir'] + if target[-1] == '\\': + target = target[0:-1] + + def clean(target): + def _clean(): + try: + shutil.rmtree(target) + except OSError: + pass + return _clean + + self.write(Action.INSTALL, self.name, ['Installing ...']) + callback = functools.partial(self.write, Action.INSTALL, self.name) + cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( + '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], + esc(target)) + com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + + def repo_uri(self): + cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' + command = Command(cmd, self.args['dir'], G_TIMEOUT,) + result = command.execute(G_RETRIES) + return result[-1] + + def update(self): + actual_uri = self.repo_uri() + expect_uri = self.args['uri'] + regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') + ma = regex.match(actual_uri) + mb = regex.match(expect_uri) + if ma is None or mb is None or ma.groups() != mb.groups(): + msg = ['', + 'Invalid URI: {0}'.format(actual_uri), + 'Expected {0}'.format(expect_uri), + 'PlugClean required.'] + raise InvalidURI(msg) + + if G_PULL: + self.write(Action.UPDATE, self.name, ['Updating ...']) + callback = functools.partial(self.write, Action.UPDATE, self.name) + fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' + cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) + com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) + result = com.execute(G_RETRIES) + self.write(Action.DONE, self.name, result[-1:]) + else: + self.write(Action.DONE, self.name, ['Already installed']) + + def write(self, action, name, msg): + self.buf_q.put((action, name, msg)) + +class PlugThread(thr.Thread): + def __init__(self, tname, args): + super(PlugThread, self).__init__() + self.tname = tname + self.args = args + + def run(self): + thr.current_thread().name = self.tname + buf_q, work_q, lock = self.args + + try: + while not G_STOP.is_set(): + name, args = work_q.get_nowait() + plug = Plugin(name, args, buf_q, lock) + plug.manage() + work_q.task_done() + except queue.Empty: + pass + +class RefreshThread(thr.Thread): + def __init__(self, lock): + super(RefreshThread, self).__init__() + self.lock = lock + self.running = True + + def run(self): + while self.running: + with self.lock: + thread_vim_command('noautocmd normal! a') + time.sleep(0.33) + + def stop(self): + self.running = False + +if G_NVIM: + def thread_vim_command(cmd): + vim.session.threadsafe_call(lambda: vim.command(cmd)) +else: + def thread_vim_command(cmd): + vim.command(cmd) + +def esc(name): + return '"' + name.replace('"', '\"') + '"' + +def nonblock_read(fname): + """ Read a file with nonblock flag. Return the last line. """ + fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) + buf = os.read(fread, 100000).decode('utf-8', 'replace') + os.close(fread) + + line = buf.rstrip('\r\n') + left = max(line.rfind('\r'), line.rfind('\n')) + if left != -1: + left += 1 + line = line[left:] + + return line + +def main(): + thr.current_thread().name = 'main' + nthreads = int(vim.eval('s:update.threads')) + plugs = vim.eval('s:update.todo') + mac_gui = vim.eval('s:mac_gui') == '1' + + lock = thr.Lock() + buf = Buffer(lock, len(plugs), G_PULL) + buf_q, work_q = queue.Queue(), queue.Queue() + for work in plugs.items(): + work_q.put(work) + + start_cnt = thr.active_count() + for num in range(nthreads): + tname = 'PlugT-{0:02}'.format(num) + thread = PlugThread(tname, (buf_q, work_q, lock)) + thread.start() + if mac_gui: + rthread = RefreshThread(lock) + rthread.start() + + while not buf_q.empty() or thr.active_count() != start_cnt: + try: + action, name, msg = buf_q.get(True, 0.25) + buf.write(action, name, ['OK'] if not msg else msg) + buf_q.task_done() + except queue.Empty: + pass + except KeyboardInterrupt: + G_STOP.set() + + if mac_gui: + rthread.stop() + rthread.join() + +main() +EOF +endfunction + +function! s:update_ruby() + ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char.chr + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + + def esc arg + %["#{arg.gsub('"', '\"')}"] + end + + def killall pid + pids = [pid] + if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM + pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } + else + unless `which pgrep 2> /dev/null`.empty? + children = pids + until children.empty? + children = children.map { |pid| + `pgrep -P #{pid}`.lines.map { |l| l.chomp } + }.flatten + pids += children + end + end + pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } + end + end + + def compare_git_uri a, b + regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} + regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) + end + + require 'thread' + require 'fileutils' + require 'timeout' + running = true + iswin = VIM::evaluate('s:is_win').to_i == 1 + pull = VIM::evaluate('s:update.pull').to_i == 1 + base = VIM::evaluate('g:plug_home') + all = VIM::evaluate('s:update.todo') + limit = VIM::evaluate('get(g:, "plug_timeout", 60)') + tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 + nthr = VIM::evaluate('s:update.threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i + vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ + cd = iswin ? 'cd /d' : 'cd' + tot = VIM::evaluate('len(s:update.todo)') || 0 + bar = '' + skip = 'Already installed' + mtx = Mutex.new + take1 = proc { mtx.synchronize { running && all.shift } } + logh = proc { + cnt = bar.length + $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" + $curbuf[2] = '[' + bar.ljust(tot) + ']' + VIM::command('normal! 2G') + VIM::command('redraw') + } + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| + mtx.synchronize do + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else + VIM::command("call add(s:update.errors, '#{name}')") + 'x' + end + result = + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] + elsif result =~ /^Interrupted|^Timeout/ + ["#{b} #{name}: #{result}"] + else + ["#{b} #{name}"] + result.lines.map { |l| " " << l } + end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end + result.each_with_index do |line, offset| + $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) + end + logh.call + end + } + bt = proc { |cmd, name, type, cleanup| + tried = timeout = 0 + begin + tried += 1 + timeout += limit + fd = nil + data = '' + if iswin + Timeout::timeout(timeout) do + tmp = VIM::evaluate('tempname()') + system("(#{cmd}) > #{tmp}") + data = File.read(tmp).chomp + File.unlink tmp rescue nil + end + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(timeout) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close + end + [$? == 0, data.chomp] + rescue Timeout::Error, Interrupt => e + if fd && !fd.closed? + killall fd.pid + fd.close + end + cleanup.call if cleanup + if e.is_a?(Timeout::Error) && tried < tries + 3.downto(1) do |countdown| + s = countdown > 1 ? 's' : '' + log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type + sleep 1 + end + log.call name, 'Retrying ...', type + retry + end + [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] + end + } + main = Thread.current + threads = [] + watcher = Thread.new { + if vim7 + while VIM::evaluate('getchar(1)') + sleep 0.1 + end + else + require 'io/console' # >= Ruby 1.9 + nil until IO.console.getch == 3.chr + end + mtx.synchronize do + running = false + threads.each { |t| t.raise Interrupt } unless vim7 + end + threads.each { |t| t.join rescue nil } + main.kill + } + refresh = Thread.new { + while true + mtx.synchronize do + break unless running + VIM::command('noautocmd normal! a') + end + sleep 0.2 + end + } if VIM::evaluate('s:mac_gui') == 1 + + clone_opt = VIM::evaluate('s:clone_opt') + progress = VIM::evaluate('s:progress_opt(1)') + nthr.times do + mtx.synchronize do + threads << Thread.new { + while pair = take1.call + name = pair.first + dir, uri, tag = pair.last.values_at *%w[dir uri tag] + exists = File.directory? dir + ok, result = + if exists + chdir = "#{cd} #{iswin ? dir : esc(dir)}" + ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil + current_uri = data.lines.to_a.last + if !ret + if data =~ /^Interrupted|^Timeout/ + [false, data] + else + [false, [data.chomp, "PlugClean required."].join($/)] + end + elsif !compare_git_uri(current_uri, uri) + [false, ["Invalid URI: #{current_uri}", + "Expected: #{uri}", + "PlugClean required."].join($/)] + else + if pull + log.call name, 'Updating ...', :update + fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' + bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil + else + [true, skip] + end + end + else + d = esc dir.sub(%r{[\\/]+$}, '') + log.call name, 'Installing ...', :install + bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { + FileUtils.rm_rf dir + } + end + mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok + log.call name, result, ok + end + } if running + end + end + threads.each { |t| t.join rescue nil } + logh.call + refresh.kill if refresh + watcher.kill +EOF +endfunction + +function! s:shellesc_cmd(arg, 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 + +function! plug#shellescape(arg, ...) + let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} + let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') + let script = get(opts, 'script', 1) + if shell =~# 'cmd\.exe' + return s:shellesc_cmd(a:arg, script) + elseif 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) + let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd + if s:is_win + let [batchfile, cmd] = s:batchfile(cmd) + endif + return system(cmd) + finally + let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] + if s:is_win && filereadable(batchfile) + call delete(batchfile) + endif + endtry +endfunction + +function! s:system_chomp(...) + let ret = call('s:system', a:000) + return v:shell_error ? '' : substitute(ret, '\n$', '', '') +endfunction + +function! s:git_validate(spec, check_branch) + let err = '' + if isdirectory(a:spec.dir) + let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir)) + let remote = result[-1] + if v:shell_error + let err = join([remote, 'PlugClean required.'], "\n") + elseif !s:compare_git_uri(remote, a:spec.uri) + let err = join(['Invalid URI: '.remote, + \ 'Expected: '.a:spec.uri, + \ 'PlugClean required.'], "\n") + elseif a:check_branch && has_key(a:spec, 'commit') + let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) + let sha = result[-1] + if v:shell_error + let err = join(add(result, 'PlugClean required.'), "\n") + elseif !s:hash_match(sha, a:spec.commit) + let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', + \ a:spec.commit[:6], sha[:6]), + \ 'PlugUpdate required.'], "\n") + endif + elseif a:check_branch + let branch = result[0] + " Check tag + if has_key(a:spec, 'tag') + let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) + if a:spec.tag !=# tag && a:spec.tag !~ '\*' + let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', + \ (empty(tag) ? 'N/A' : tag), a:spec.tag) + endif + " Check branch + elseif a:spec.branch !=# branch + let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', + \ branch, a:spec.branch) + endif + if empty(err) + let [ahead, behind] = split(s:lastline(s:system(printf( + \ 'git rev-list --count --left-right HEAD...origin/%s', + \ a:spec.branch), a:spec.dir)), '\t') + if !v:shell_error && ahead + if behind + " Only mention PlugClean if diverged, otherwise it's likely to be + " pushable (and probably not that messed up). + let err = printf( + \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" + \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) + else + let err = printf("Ahead of origin/%s by %d commit(s).\n" + \ .'Cannot update until local changes are pushed.', + \ a:spec.branch, ahead) + endif + endif + endif + endif + else + let err = 'Not found' + endif + return [err, err =~# 'PlugClean'] +endfunction + +function! s:rm_rf(dir) + if isdirectory(a:dir) + call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . plug#shellescape(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 + while l1 <= l2 + let line = getline(l1) + if line =~ '^- ' && isdirectory(line[2:]) + execute l1 + redraw! + let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) + let force = force || answer > 1 + if answer + call s:rm_rf(line[2:]) + setlocal modifiable + call setline(l1, '~'.line[1:]) + let s:clean_count += 1 + call setline(4, printf('Removed %d directories.', s:clean_count)) + setlocal nomodifiable + endif + endif + let l1 += 1 + endwhile +endfunction + +function! s:upgrade() + echo 'Downloading the latest version of vim-plug' + redraw + let tmp = s:plug_tempname() + let new = tmp . '/plug.vim' + + try + let out = s:system(printf('git clone --depth 1 %s %s', plug#shellescape(s:plug_src), plug#shellescape(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 range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' + let cmd = 'git log --graph --color=never ' + \ . (s:git_version_requirement(2, 10, 0) ? '--no-show-signature ' : '') + \ . join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 'plug#shellescape(v:val)')) + if has_key(v, 'rtp') + let cmd .= ' -- '.plug#shellescape(v.rtp) + endif + let diff = s:system_chomp(cmd, v.dir) + if !empty(diff) + let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' + call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) + let cnts[origin] += 1 + endif + let bar .= '=' + call s:progress_bar(2, bar, len(total)) + normal! 2G + redraw + endfor + if !cnts[origin] + call append(5, ['', 'N/A']) + endif + endfor + call setline(1, printf('%d plugin(s) updated.', cnts[0]) + \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) + + if cnts[0] || cnts[1] + nnoremap (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:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) + if !empty(sha) + call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) + redraw + endif + endfor + + if a:0 > 0 + let fn = s:plug_expand(a:1) + if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) + return + endif + call writefile(getline(1, '$'), fn) + echo 'Saved as '.a:1 + silent execute 'e' s:esc(fn) + setf vim + endif +endfunction + +function! s:split_rtp() + return split(&rtp, '\\\@ +map +map +map + +" plugins +call plug#begin() +Plug 'arcticicestudio/nord-vim' +Plug 'scrooloose/nerdtree' +Plug 'maxmellon/vim-jsx-pretty' +Plug 'ryanoasis/vim-devicons' +Plug 'junegunn/fzf', { 'do': './install --bin' } +Plug 'junegunn/fzf.vim' +Plug 'neoclide/coc.nvim', {'branch': 'release'} +Plug 'leafgarland/typescript-vim' +Plug 'peitalin/vim-jsx-typescript' +call plug#end() + +" theme +colorscheme nord + +" NERDTree +let g:NERDTreeShowHidden = 1 +let g:NERDTreeMinimalUI = 1 +let g:NERDTreeIgnore = [] +let g:NERDTreeStatusline = '' + +" fzf - use silversearcher-ag to respect .gitignore +let $FZF_DEFAULT_COMMAND = 'ag -g ""' + +" plugin keybindings +nnoremap :NERDTreeToggle +nnoremap :FZF +let g:fzf_action = { + \ 'ctrl-t': 'tab split', + \ 'ctrl-s': 'split', + \ 'ctrl-v': 'vsplit' + \} From d640ba3d34d4fdc4955e92c7accb4467710683cf Mon Sep 17 00:00:00 2001 From: Hektor Misplon Date: Sun, 16 Feb 2020 08:53:21 +0000 Subject: [PATCH 002/893] Add suckless tools w/ config --- .suckless/dmenu | 1 + .suckless/dwm | 1 + .suckless/sselp | 1 + .suckless/st | 1 + 4 files changed, 4 insertions(+) create mode 160000 .suckless/dmenu create mode 160000 .suckless/dwm create mode 160000 .suckless/sselp create mode 160000 .suckless/st diff --git a/.suckless/dmenu b/.suckless/dmenu new file mode 160000 index 0000000..db6093f --- /dev/null +++ b/.suckless/dmenu @@ -0,0 +1 @@ +Subproject commit db6093f6ec1bb884f7540f2512935b5254750b30 diff --git a/.suckless/dwm b/.suckless/dwm new file mode 160000 index 0000000..2dbce7f --- /dev/null +++ b/.suckless/dwm @@ -0,0 +1 @@ +Subproject commit 2dbce7fc0bfdfa43afba70481667eb73f173c200 diff --git a/.suckless/sselp b/.suckless/sselp new file mode 160000 index 0000000..b99d41a --- /dev/null +++ b/.suckless/sselp @@ -0,0 +1 @@ +Subproject commit b99d41a77db44418fcfbdd2cdebdb08adb3a7ca3 diff --git a/.suckless/st b/.suckless/st new file mode 160000 index 0000000..e910946 --- /dev/null +++ b/.suckless/st @@ -0,0 +1 @@ +Subproject commit e910946caf878d6939932cf4cc924e7fde5916b5 From 56d1a37906ed51810ca23ffa81b4c6701db7bc00 Mon Sep 17 00:00:00 2001 From: Hektor Misplon <36757790+hektormisplon@users.noreply.github.com> Date: Sun, 16 Feb 2020 08:16:47 +0000 Subject: [PATCH 003/893] Create README.md --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..679c721 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# dotfiles + +## tools + +### suckless + +- dwm +- dmenu +- st +- sselp + +### vim + +- vim-plug +- NERDTree +- FZF +- coc From 06a838e7231ddb3ef60dedecbfa8a15374188746 Mon Sep 17 00:00:00 2001 From: Hektor Misplon <36757790+hektormisplon@users.noreply.github.com> Date: Sun, 16 Feb 2020 08:19:32 +0000 Subject: [PATCH 004/893] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 679c721..e898503 100644 --- a/README.md +++ b/README.md @@ -15,3 +15,11 @@ - NERDTree - FZF - coc +- javascript, typescript & react + - typescript-vim + - vim-jsx-pretty + - vim-jsx-typescript + +## themes + +- nord From 90315ab61ebcbe9091f68cfc17366aea825d739a Mon Sep 17 00:00:00 2001 From: Hektor Misplon Date: Sun, 16 Feb 2020 09:27:34 +0000 Subject: [PATCH 005/893] Convert submodules to directories --- .suckless/dmenu | 1 - .suckless/dmenu/.gitignore | 1 + .suckless/dmenu/LICENSE | 30 + .suckless/dmenu/Makefile | 64 + .suckless/dmenu/README | 24 + .suckless/dmenu/arg.h | 49 + .suckless/dmenu/config.def.h | 23 + .suckless/dmenu/config.h | 23 + .suckless/dmenu/config.mk | 31 + .suckless/dmenu/dmenu | Bin 0 -> 43888 bytes .suckless/dmenu/dmenu.1 | 194 ++ .suckless/dmenu/dmenu.c | 771 ++++++++ .suckless/dmenu/dmenu.o | Bin 0 -> 32520 bytes .suckless/dmenu/dmenu_path | 13 + .suckless/dmenu/dmenu_run | 2 + .suckless/dmenu/drw.c | 435 +++++ .suckless/dmenu/drw.h | 57 + .suckless/dmenu/drw.o | Bin 0 -> 10488 bytes .suckless/dmenu/stest | Bin 0 -> 17736 bytes .suckless/dmenu/stest.1 | 90 + .suckless/dmenu/stest.c | 109 ++ .suckless/dmenu/stest.o | Bin 0 -> 5312 bytes .suckless/dmenu/util.c | 35 + .suckless/dmenu/util.h | 8 + .suckless/dmenu/util.o | Bin 0 -> 2240 bytes .suckless/dwm | 1 - .suckless/dwm/.gitignore | 1 + .suckless/dwm/LICENSE | 37 + .suckless/dwm/Makefile | 51 + .suckless/dwm/README | 48 + .suckless/dwm/config.def.h | 115 ++ .suckless/dwm/config.h | 114 ++ .suckless/dwm/config.mk | 38 + .suckless/dwm/drw.c | 435 +++++ .suckless/dwm/drw.h | 57 + .suckless/dwm/drw.o | Bin 0 -> 10488 bytes .suckless/dwm/dwm | Bin 0 -> 62304 bytes .suckless/dwm/dwm.1 | 176 ++ .suckless/dwm/dwm.c | 2149 +++++++++++++++++++++ .suckless/dwm/dwm.o | Bin 0 -> 57536 bytes .suckless/dwm/dwm.png | Bin 0 -> 373 bytes .suckless/dwm/transient.c | 42 + .suckless/dwm/util.c | 35 + .suckless/dwm/util.h | 8 + .suckless/dwm/util.o | Bin 0 -> 2240 bytes .suckless/sselp | 1 - .suckless/sselp/.gitignore | 1 + .suckless/sselp/.hgtags | 2 + .suckless/sselp/LICENSE | 21 + .suckless/sselp/Makefile | 48 + .suckless/sselp/README | 24 + .suckless/sselp/config.mk | 23 + .suckless/sselp/sselp | Bin 0 -> 14480 bytes .suckless/sselp/sselp.c | 45 + .suckless/sselp/sselp.o | Bin 0 -> 3256 bytes .suckless/st | 1 - .suckless/st/.gitignore | 1 + .suckless/st/FAQ | 195 ++ .suckless/st/LEGACY | 17 + .suckless/st/LICENSE | 34 + .suckless/st/Makefile | 57 + .suckless/st/README | 34 + .suckless/st/TODO | 28 + .suckless/st/arg.h | 50 + .suckless/st/config.def.h | 459 +++++ .suckless/st/config.h | 460 +++++ .suckless/st/config.mk | 35 + .suckless/st/st | Bin 0 -> 102256 bytes .suckless/st/st-anysize-0.8.1.diff | 152 ++ .suckless/st/st-nordtheme-0.8.2.diff | 100 + .suckless/st/st.1 | 176 ++ .suckless/st/st.c | 2599 ++++++++++++++++++++++++++ .suckless/st/st.h | 123 ++ .suckless/st/st.info | 222 +++ .suckless/st/st.o | Bin 0 -> 73200 bytes .suckless/st/win.h | 39 + .suckless/st/x.c | 2045 ++++++++++++++++++++ .suckless/st/x.o | Bin 0 -> 73912 bytes 78 files changed, 12255 insertions(+), 4 deletions(-) delete mode 160000 .suckless/dmenu create mode 100644 .suckless/dmenu/.gitignore create mode 100644 .suckless/dmenu/LICENSE create mode 100644 .suckless/dmenu/Makefile create mode 100644 .suckless/dmenu/README create mode 100644 .suckless/dmenu/arg.h create mode 100644 .suckless/dmenu/config.def.h create mode 100644 .suckless/dmenu/config.h create mode 100644 .suckless/dmenu/config.mk create mode 100755 .suckless/dmenu/dmenu create mode 100644 .suckless/dmenu/dmenu.1 create mode 100644 .suckless/dmenu/dmenu.c create mode 100644 .suckless/dmenu/dmenu.o create mode 100755 .suckless/dmenu/dmenu_path create mode 100755 .suckless/dmenu/dmenu_run create mode 100644 .suckless/dmenu/drw.c create mode 100644 .suckless/dmenu/drw.h create mode 100644 .suckless/dmenu/drw.o create mode 100755 .suckless/dmenu/stest create mode 100644 .suckless/dmenu/stest.1 create mode 100644 .suckless/dmenu/stest.c create mode 100644 .suckless/dmenu/stest.o create mode 100644 .suckless/dmenu/util.c create mode 100644 .suckless/dmenu/util.h create mode 100644 .suckless/dmenu/util.o delete mode 160000 .suckless/dwm create mode 100644 .suckless/dwm/.gitignore create mode 100644 .suckless/dwm/LICENSE create mode 100644 .suckless/dwm/Makefile create mode 100644 .suckless/dwm/README create mode 100644 .suckless/dwm/config.def.h create mode 100644 .suckless/dwm/config.h create mode 100644 .suckless/dwm/config.mk create mode 100644 .suckless/dwm/drw.c create mode 100644 .suckless/dwm/drw.h create mode 100644 .suckless/dwm/drw.o create mode 100755 .suckless/dwm/dwm create mode 100644 .suckless/dwm/dwm.1 create mode 100644 .suckless/dwm/dwm.c create mode 100644 .suckless/dwm/dwm.o create mode 100644 .suckless/dwm/dwm.png create mode 100644 .suckless/dwm/transient.c create mode 100644 .suckless/dwm/util.c create mode 100644 .suckless/dwm/util.h create mode 100644 .suckless/dwm/util.o delete mode 160000 .suckless/sselp create mode 100644 .suckless/sselp/.gitignore create mode 100644 .suckless/sselp/.hgtags create mode 100644 .suckless/sselp/LICENSE create mode 100644 .suckless/sselp/Makefile create mode 100644 .suckless/sselp/README create mode 100644 .suckless/sselp/config.mk create mode 100755 .suckless/sselp/sselp create mode 100644 .suckless/sselp/sselp.c create mode 100644 .suckless/sselp/sselp.o delete mode 160000 .suckless/st create mode 100644 .suckless/st/.gitignore create mode 100644 .suckless/st/FAQ create mode 100644 .suckless/st/LEGACY create mode 100644 .suckless/st/LICENSE create mode 100644 .suckless/st/Makefile create mode 100644 .suckless/st/README create mode 100644 .suckless/st/TODO create mode 100644 .suckless/st/arg.h create mode 100644 .suckless/st/config.def.h create mode 100644 .suckless/st/config.h create mode 100644 .suckless/st/config.mk create mode 100755 .suckless/st/st create mode 100644 .suckless/st/st-anysize-0.8.1.diff create mode 100644 .suckless/st/st-nordtheme-0.8.2.diff create mode 100644 .suckless/st/st.1 create mode 100644 .suckless/st/st.c create mode 100644 .suckless/st/st.h create mode 100644 .suckless/st/st.info create mode 100644 .suckless/st/st.o create mode 100644 .suckless/st/win.h create mode 100644 .suckless/st/x.c create mode 100644 .suckless/st/x.o diff --git a/.suckless/dmenu b/.suckless/dmenu deleted file mode 160000 index db6093f..0000000 --- a/.suckless/dmenu +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db6093f6ec1bb884f7540f2512935b5254750b30 diff --git a/.suckless/dmenu/.gitignore b/.suckless/dmenu/.gitignore new file mode 100644 index 0000000..c190512 --- /dev/null +++ b/.suckless/dmenu/.gitignore @@ -0,0 +1 @@ +*.backup diff --git a/.suckless/dmenu/LICENSE b/.suckless/dmenu/LICENSE new file mode 100644 index 0000000..9762166 --- /dev/null +++ b/.suckless/dmenu/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2019 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/.suckless/dmenu/Makefile b/.suckless/dmenu/Makefile new file mode 100644 index 0000000..a03a95c --- /dev/null +++ b/.suckless/dmenu/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all options clean dist install uninstall diff --git a/.suckless/dmenu/README b/.suckless/dmenu/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/.suckless/dmenu/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/.suckless/dmenu/arg.h b/.suckless/dmenu/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/.suckless/dmenu/arg.h @@ -0,0 +1,49 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/.suckless/dmenu/config.def.h b/.suckless/dmenu/config.def.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/.suckless/dmenu/config.def.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/.suckless/dmenu/config.h b/.suckless/dmenu/config.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/.suckless/dmenu/config.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/.suckless/dmenu/config.mk b/.suckless/dmenu/config.mk new file mode 100644 index 0000000..0929b4a --- /dev/null +++ b/.suckless/dmenu/config.mk @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 4.9 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/.suckless/dmenu/dmenu b/.suckless/dmenu/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..e85b0b5010e0a5f84f6948361dc00d8a4d5aea4c GIT binary patch literal 43888 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 YhgPqKU4+DN +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + movewordedge(-1); + goto draw; + case XK_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl(*buf)) + insert(buf, len); + break; + case XK_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + match(); + break; + } + +draw: + drawmenu(); +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = MIN(inputw, mw/3); + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/.suckless/dmenu/dmenu.o b/.suckless/dmenu/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..e0b153e57bf837fcef82d8d541ce076bffb9b5b1 GIT binary patch literal 32520 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 literal 0 HcmV?d00001 diff --git a/.suckless/dmenu/dmenu_path b/.suckless/dmenu/dmenu_path new file mode 100755 index 0000000..3a7cda7 --- /dev/null +++ b/.suckless/dmenu/dmenu_path @@ -0,0 +1,13 @@ +#!/bin/sh + +cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" +cache="$cachedir/dmenu_run" + +[ ! -e "$cachedir" ] && mkdir -p "$cachedir" + +IFS=: +if stest -dqr -n "$cache" $PATH; then + stest -flx $PATH | sort -u | tee "$cache" +else + cat "$cache" +fi diff --git a/.suckless/dmenu/dmenu_run b/.suckless/dmenu/dmenu_run new file mode 100755 index 0000000..834ede5 --- /dev/null +++ b/.suckless/dmenu/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/.suckless/dmenu/drw.c b/.suckless/dmenu/drw.c new file mode 100644 index 0000000..8fd1ca4 --- /dev/null +++ b/.suckless/dmenu/drw.c @@ -0,0 +1,435 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Wrapper to create color schemes. The caller has to call free(3) on the + * returned color scheme when done using it. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + int utf8strlen, utf8charlen, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + while (1) { + utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/.suckless/dmenu/drw.h b/.suckless/dmenu/drw.h new file mode 100644 index 0000000..4c67419 --- /dev/null +++ b/.suckless/dmenu/drw.h @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/.suckless/dmenu/drw.o b/.suckless/dmenu/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..622000761139598e51783f5f8042982d8ad43752 GIT binary patch literal 10488 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 T5rwsPEn**K!2V3Vi`)MX)B1ZN literal 0 HcmV?d00001 diff --git a/.suckless/dmenu/stest b/.suckless/dmenu/stest new file mode 100755 index 0000000000000000000000000000000000000000..3ec07e4b602f583e119822059795c08c42791114 GIT binary patch literal 17736 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 p(x&sK@e|h=QQh_SgZQEX(uhGw