Writing Appropriately

職務経歴

Vim シンプルなVimプラグインマネージャを書いた

既存のプラグインマネージャー使用した状態で自分の書くプラグインの修正に手間取ったり高機能すぎて何をしているのか把握できなかったり起動が遅くなったりしたのでシンプルなVimプラグインマネージャを書いた
あとプラグインの設定もプラグインマネージャにaddした直後に書きたかった
Linuxで複数ユーザーでいろいろとテストをしたいのでg:spm_repodirに共有ディレクトリを設定してレポジトリは一箇所に置いているがうまくいっている
自分が自分の書いたプラグインマネージャを使っている事を忘れるぐらいにはちゃんとしている
gvimのmru.vimでmenu.vim:120行あたりのhas("printer")のセクションで文句言うもんでコメントアウトしといたぐらい

Simple Plugin Manager で spm

開発はIntelliJ使ってます Vim は趣味です

概要

  • クローンしてruntimepathに追加するだけ
  • silient executeやvim#procで非同期など難しい事をしてないので全てが"見える"

動作確認

OS APP res
CentOS7.5 Neovim 0.2.2
macOS High Sierra MacVim 8.0
macOS High Sierra Neovim 0.2.2
Windows10 KaoriYa Vim 8.0

functions

function action
call spm#clone(url) urlを'git clone {url} {uri}'する
{uri}はg:spm_repodirのディレクトリの位置+uri
~/.vim/repos/github.com/naoyuki1019/vim-spmみたいな
call spm#clone() vimrcに書いてある全てのURLを'git clone {url}'する
call spm#pull(string) clone済みのレポジトリを'git pull'する
stringにnaoyukiと入れるとurlにnaoyukiを含むレポジトリをpullするか一つ一つconfirmが出る
call spm#pull() spm#clone(url)で設定されている全てのレポジトリを'git pull'する
一つ一つconfirmが出る
call spm#status() レポジトリのclone状況が確認できる

spm#status()は新規レポジトリをcloneしたときもVimEnterで実行される f:id:naoyuki1019:20180602171557p:plain

削除は?

削除は自分は必要ない

消す時は:!rm -rf xxxxxでいい

Setting

add .vimrc

let g:spm_repodir = '~/.vim/repos'

let s:spm_dir = '~/.vim/spm'
if filereadable(fnamemodify(s:spm_dir,':p').'autoload/spm.vim') "{{{
  execute 'set runtimepath+='.s:spm_dir

  "---------------------------------------------------------
  " plugin 1
  call spm#clone('https://github.com/Shougo/unite.vim')
  " plugin 1 settings
  let g:unite_enable_start_insert = 0

  " plugin 2
  call spm#clone('https://github.com/Shougo/neomru.vim')
  " plugin 2 settings
  let g:unite_source_file_mru_limit = 1000

  " plugin 3
  call spm#clone('https://github.com/thinca/vim-qfreplace')
  " plugin 3 settings
  let ......

  " plugin 4
  call spm#clone('https://github.com/tacroe/unite-mark')
  " plugin 4 settings
  let ......


  ....
  ...
  ..
  .
  "---------------------------------------------------------

endif

....
...
..
.

filetype plugin indent on
syntax enable

Github

github.com

ソースコードそのまま

scriptencoding utf-8
"/**
" * @file spm.vim
" * @author naoyuki onishi <naoyuki1019 at gmail.com>
" * @version 1.0
" */

if exists("g:loaded_spm")
  finish
endif
let g:loaded_spm = 1

let s:save_cpo = &cpo
set cpo&vim

if has("win32") || has("win95") || has("win64") || has("win16")
  let s:is_win = 1
  let s:ds = '\'
else
  let s:is_win = 0
  let s:ds = '/'
endif

if !exists('g:spm_repodir')
  let g:spm_repodir = fnamemodify(expand('<sfile>:p:h').'/../repos', ':p')
endif

let g:spm_dict = {}
let s:loaded = 0
let s:clone_onload_list = []
let s:clone_list = []

augroup augroup#SPM
  autocmd!
  autocmd VimEnter * call s:VimEnter()
augroup END

function spm#status()
  let l:list = []
  for [l:url, l:dict] in items(g:spm_dict)
    call add(l:list, l:url)
  endfor
  call sort(l:list)
  call s:show_clone_info(l:list)
endfunction

function s:VimEnter()
  let s:loaded = 1
  call s:show_clone_info(s:clone_onload_list)
endfunction

function s:show_clone_info(list) "{{{
  let l:list = a:list
  if 0 < len(a:list)
    let l:msg = []
    call add(l:msg, '"--------------------------------------------------')
    call add(l:msg, '" Simple Plugin Manager')
    call add(l:msg, '" local: '.g:spm_repodir )
    call add(l:msg, '"--------------------------------------------------')
    for l:url in a:list
      call add(l:msg, '       |')
      call add(l:msg, 'remote | '.l:url)
      call add(l:msg, 'message| '.g:spm_dict[l:url]['msg'])
    endfor
    call add(l:msg, '       |')
    if has('gui_running') && 1 == s:is_win
      call confirm(join(l:msg, "\n"))
    else
      echo join(l:msg, "\n")
    endif
  endif
endfunction "}}}

function! s:uri(url)
  let l:n = match(a:url, '://')
  let l:dir = a:url[l:n+3:]
  return substitute(l:dir, ':', '-', "g")
endfunction

function! s:fix_ds(path)
  let l:path = a:path
  let l:path = substitute(l:path, '\v\/{2,}', '/', 'g')
  if 1 == s:is_win
    let l:path = substitute(l:path, '\/', '\\', 'g')
  endif
  return l:path
endfunction

function! s:rm_tail_ds(dir)
  let l:dir = a:dir
  let l:len = strlen(a:dir)
  let l:tail = l:dir[l:len-1]
  if '/' == l:tail || '\' == l:tail
    let l:dir = l:dir[0:l:len-2]
  endif
  return l:dir
endfunction

function! s:add_tail_ds(dir)
  let l:dir = a:dir
  let l:len = strlen(a:dir)
  let l:tail = l:dir[l:len-1]
  if '/' != l:tail && '\' != l:tail
    let l:dir .= s:ds
  endif
  return l:dir
endfunction

function! spm#pull(...) "{{{

  if 1 > a:0
    let l:s = '.*'
  else
    let l:s = join(a:000, ' ')
    let l:s = substitute(l:s, '\v([^\.])\*', '\1.\*', 'g')
    let l:s = substitute(l:s, '\v([^\\])\.([^\*])', '\\.', 'g')
    let l:s = substitute(l:s, '\v\s{1,}', '.*', 'g')
  endif

  let l:unmatchall = 1

  for [l:url, l:dict] in items(g:spm_dict)

    let l:n = match(l:url, l:s)
    if -1 < l:n
      let l:unmatchall = 0

      let l:dir = fnamemodify(l:dict['dir'], ':p')
      if 1 == s:is_win
        let l:drive = l:dir[:stridx(l:dir, ':')]
        let l:execute = '!'.l:drive.' & cd '.shellescape(l:dir).' & git pull'
      else
        let l:execute = '!cd '.shellescape(l:dir).'; git pull'
      endif

      let l:conf = confirm('execute? ['.l:execute.']', "Yyes\nNno")
      if 1 != l:conf
        continue
      endif

      execute l:execute

    endif
  endfor

  if 1 == l:unmatchall
    call confirm('there was no match url')
  endif

endfunction "}}}

function! spm#clone(...)

  if 1 > a:0
    let s:clone_list = []
    for [l:url, l:dict] in items(g:spm_dict)
      call spm#clone(l:url)
    endfor
    call s:show_clone_info(s:clone_list)
    return
  endif

  let l:url = a:000[0]

  let l:dir = g:spm_repodir.'/'.s:uri(l:url)
  let l:dir = s:fix_ds(l:dir)
  let l:dir = fnamemodify(l:dir, ':p')
  let l:dir = s:rm_tail_ds(l:dir)
  let g:spm_dict[l:url] = {
        \'dir': l:dir,
        \'sts': 0,
        \'msg': ''
        \}

  call add(s:clone_list, l:url)

  if !isdirectory(s:add_tail_ds(g:spm_dict[l:url]['dir']).'.git')
    call add(s:clone_onload_list, l:url)
    let l:git_clone = s:git_clone(l:url, g:spm_dict[l:url]['dir'])
    if 0 != l:git_clone
      return
    endif
  else
    let g:spm_dict[l:url]['sts'] = 0
    let g:spm_dict[l:url]['msg'] = 'installed'
  endif

  exec 'set runtimepath+='.g:spm_dict[l:url]['dir']

endfunction

function! s:git_clone(url, dir)

  let l:escaped_url = shellescape(a:url)
  let l:escaped_dir = shellescape(s:rm_tail_ds(a:dir))
  let l:execute = '!git clone '.l:escaped_url.' '.l:escaped_dir

  if 1 == s:loaded || ( 0 == s:loaded && !has('gui_running') && !has('nvim'))
    let l:conf = confirm('execute? ['.l:execute.']', "Yyes\nNno")
    if 1 != l:conf
      let g:spm_dict[a:url]['sts'] = 2
      let g:spm_dict[a:url]['msg'] = 'clone: canceled'
      return 2
    endif
  endif

  if has('gui_running') && 1 == s:is_win
    silent execute l:execute
  else
    execute l:execute
  endif

  if !isdirectory(s:add_tail_ds(a:dir).'.git')
    let g:spm_dict[a:url]['sts'] = 1
    let g:spm_dict[a:url]['msg'] = 'clone: an error occurred. plz check url'
    if 1 == s:loaded
      call confirm('an error occurred. plz check url')
    endif
    return 1
  endif

  let g:spm_dict[a:url]['sts'] = 0
  let g:spm_dict[a:url]['msg'] = 'clone: success'

  return 0

endfunction


let &cpo = s:save_cpo
unlet s:save_cpo