koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

Vimで作成する新規ファイルのパーミッションを自動的に変更する

僕はWindowsを用いる場合,KaoriYagvimと,Cygwinvimを用いている. そして,Cygwinのgitを利用している. Cygwinであれば,新規に作成されるファイルのパーミッションは644であるが,Windowsだとパーミッションが700となるのが気にくわなかった. というのも,gitはファイルのパーミッションも含めて管理するし,GitHub上では実行可能なテキストファイル等(例えばLICENSE)は,シェルスクリプトシンタックスハイライトが適用されるからだ.

無意味な実行権限の付与は避けたいので,Windows環境でVimで新規ファイルを作成するときに,Cygwin等のchmodを呼び出し,ファイルのパーミッションを変更する処理を加えることにした. ただし,ファイル先頭がshebang行であるならば,パーミッションを755にする.

function! s:system(cmd)
  try
    call vimproc#cmd#system(a:cmd)
  catch /^Vim(call)\=:E117: .\+: vimproc#cmd#system$/
    call system(a:cmd)
  endtry
endfunction

if executable('chmod')
  augroup Permission
    autocmd!
  augroup END
  if has('win95') || has('win16') || has('win32') || has('win64')
    autocmd Permission BufNewFile *
          \ autocmd Permission BufWritePost <buffer>  call s:permission_644()
    function! s:permission_644()
      autocmd! Permission BufWritePost <buffer>
      silent! call s:system((getline(1)[0 : 1] ==# '#!' ? 'chmod 755 ' : 'chmod 644 ')
            \ . shellescape(expand('%')))
    endfunction
  else
    autocmd Permission BufNewFile *
          \ autocmd Permission BufWritePost <buffer>  call s:add_permission_x()
    function! s:add_permission_x()
      autocmd! Permission BufWritePost <buffer>
      if getline(1)[0 : 1] ==# '#!'
        silent! call s:system('chmod a+x ' . shellescape(expand('%')))
      endif
    endfunction
    autocmd MyAutoCmd BufWritePost * call s:add_permission_x()
  endif
endif

元ネタは,shebang付きファイルに実行権限を与える設定である. 以下の記事あたりが参考例だろう.

しかし,既存のファイルの実行権限を勝手に変更してしまうのもどうかと思ったので(例えshebang付きであっても),「新規ファイル作成時のみ」そのパーミッションを変更するようにした. 仕組みとしては以下のようになる.

  1. 存在しないファイルの編集を始めたとき(BufNewFileが発火したとき)に,バッファローカルでバッファ全体をファイルに書き込んだ後に発火するイベント(BufWritePost)に対し,パーミッションを変更する関数を呼び出すアクションを設定する
  2. パーミッションを変更する関数では,最初にBufWritePostに対して設定された自動コマンドを消去し,パーミッションを変更する外部コマンドをsystem()を通じて呼び出す.

BufNewFile時にBufWritePostの自動コマンドを設定しているのがポイントだ. こうすることで,新規ファイルの保存時のみパーミッションを変更することができる.

ただし,writefile()等でのファイルのパーミッションの変更には対応していない. これらは,個別にスクリプト中やコマンドを実行して対応すべきだろう. (vital.vim:VitalizeWindowsVimでやると,実行権限が付いてしまうので,Cygwinvim:Vitalizeしよう)

s:system()については,組み込み関数のsystem()でも構わないが,Windowsだと一瞬コマンドプロンプトのウインドウが開くのが気になるので,vimproc.vimがインストールされているならば,そのsystem()を用いることにする. vimproc#system()vimproc#cmd#system()の違いについては,vimproc.vimソースコードを参照するか,以下を参照して欲しい.

参考