2019/12/31


はじめに

以下の記事では、僕の Vim の構成について記述しています。本来はこの記事で vim-lsp の導入方法と私的 Go 編集環境について書こうと思っていましたが、あまりにも長くなってしまったので別途書く事にしました。僕は Windows と Linux しか使わないので、皆さんの環境で使うとうまく動かない可能性があります。また僕は最新の Vim 8 しか使いません。古めの Vim を使いません。neovim も使いません。それらをお使いの方はうまく動かない可能性があります。ご了承下さい。なお設定ファイルの配置スタイルは完全に僕個人の趣味ですので必ずしも僕の構成が正しい訳ではありません。

ぼくのかんがえたさいきょうの Vim こうせい

Vim の設定は vimrc に記述するのですが、その設定方法には「汚くさせない」ための工夫が必要だと思っています。以下は僕が行っている「vimrc を綺麗に保つ方法」です。

vimrc に記述する内容は大きく分けて以下の物があります。

  1. Vim 本体の機能のデフォルト値を変更する設定
  2. プラグインの読み込み
  3. ユーザ固有のマッピングやコマンドの定義
  4. プラグイン固有の設定
これは上記の順で書くのが良いと思います。幾らか例外はありますが、おおよそ上記の流れで書くのが良いと思います。今回、僕の vimrc を公開はしませんが、おおよそこの流れに沿っていると思って下さい。なぜ公開しないのかというと、日々スタイルを模索してどんどん変わっていく為、今回ここで紹介した物が僕の vimrc だと広まって欲しくないからです。また好き嫌いはあると思うのですが、僕は最近は分割 vimrc 形式を使っています。
vimfiles
├─vimrc
├─gvimrc
├─autoload/
├─plugged/
└─_config/

autoload には後で述べる vim-plug の plug.vim しか入っていません。上記のリストの内、1と2は vimrc および gvimrc に記述しています。残りは全て _config というフォルダ内に、各機能毎に分けて記述しています。以下は僕の vimrc の概要です。

" Vim 本体の機能のデフォルト値を経項する設定
setglobal cmdheight=2
setglobal laststatus=2

... 略 ...

setglobal fileformat=unix
setglobal formatoptions+=mb

if !has('win32') && !has('win64')
  setglobal shell=/bin/bash
endif

if exists('&termguicolors')
  setglobal termguicolors
endif

if exists('&completeslash')
  setglobal completeslash=slash
endif

let g:no_gvimrc_example=1
let g:no_vimrc_example=1

let g:loaded_gzip               = 1
let g:loaded_tar                = 1
let g:loaded_tarPlugin          = 1
let g:loaded_zip                = 1
let g:loaded_zipPlugin          = 1
let g:loaded_rrhelper           = 1
let g:loaded_vimball            = 1
let g:loaded_vimballPlugin      = 1
let g:loaded_getscript          = 1
let g:loaded_getscriptPlugin    = 1
let g:loaded_netrw              = 1
let g:loaded_netrwPlugin        = 1
let g:loaded_netrwSettings      = 1
let g:loaded_netrwFileHandlers  = 1
let g:did_install_default_menus = 1
let g:skip_loading_mswin        = 1
let g:did_install_syntax_menu   = 1
"let g:loaded_2html_plugin       = 1

let g:mapleader = '\'
let g:maplocalleader = ','

" git commit 時にはプラグインは読み込まない
if $HOME != $USERPROFILE && $GIT_EXEC_PATH != ''
  finish
end

" Windows の場合は必要なパスを追加しておく
if has('win32')
  let $PATH='c:\dev\vim;c:\msys64\mingw64\bin;c:\msys64\usr\bin;'
  \ .'c:\Program Files\Java\jdk1.8.0_221\bin;'.$PATH
endif

" プラグインの読み込み
let g:plug_shallow = 0

call plug#begin('~/.vim/plugged')

Plug 'thinca/vim-ambicmd'
Plug 'thinca/vim-openbuf'
Plug 'thinca/vim-quickrun'

... 略 ...

call plug#end()

" 各種設定の読み込み
call map(sort(split(globpath(&runtimepath, '_config/*.vim'))){->[execute('exec "so" v:val')]})

これを $HOME/.vim、Windows であれば %USERPROFILE%\vimfiles の中に vimrc というファイル名で置いています。便宜上、vimfiles を Windows のシンボリックリンクを使って .vim にリンクさせています。

気を付けるべきはこの状態を常に保つ事です。これは vimrc だけでなく .emacs.el 等にも言える事ですが、「ちょっと今だけ」と vimrc に手を加えた設定は、おそらく数か月後もそのままになってしまう可能性があるからです。綺麗な vimrc を保ち続けたいのであれば、この形は崩さない事です。

またなぜ僕がこの形にしているかというと、機能単位またはプラグイン単位で設定ファイルを消せるからです。必要なくなったプラグインの設定が vimrc に残ったままだと、どんどん汚くなってしまいます。必要に応じて消していくのも、vimrc を綺麗に保つ秘訣です。これは僕が2015年から SoftwareDesign の連載「Vim の細道」を書く上で毎月 vimrc を継ぎ足ししているうちに、どうしても vimrc が汚くなってしまった問題を解決する為に行き着いた方法です。

Vim 本体の機能のデフォルト値を変更する設定

ここで僕が言える事は「これらの設定をばらまかない事」。これらの設定は今後どんな新しいプラグインが出てきたとしても変わらない設定であるはずです。

プラグインの読み込み

僕は vim-plug というプラグインマネージャを使っています。他のプラグインマネージャを使っている方もいると思います。ちなみに僕も vim-plug に満足している訳ではなく、現在は minpac に移行しようか検討中です。

ユーザ固有のマッピングやコマンド定義

これ以降は、ユーザ固有のマッピングやコマンド定義、プラグイン固有の設定、の2つとなりますが、これらは個別の設定ファイルに書いています。例えば僕は _config ディレクトリの下に以下の様に配置しています。
000-mappings.vim
001-filetype.vim
002-commands.vim
003-path.vim
102-autofmt.vim
104-conda.vim
105-ctrlp.vim
106-emmet.vim
107-lightline.vim
200-lsp.vim
201-languageclient-nvim.vim
202-vsnip.vim
204-quickrun.vim
300-vim.vim
301-java.vim
400-memolist.vim
401-openbrowser.vim
402-previm.vim
403-termdebug.vim
404-tohtml.vim
405-twitvim.vim

おおよそ番号体系があって、000番台が Vim 本体に関する物、100番台が全体に関わるプラグインの設定、200番台以降がプラグインの設定ですがその中でもグローバルに適用される物、300番台がファイルタイプに関連する物、400番台がアプリケーションに関する物、となっています。この番号体系は完全に守れている訳ではないです。

ユーザ固有のコマンド定義では、コマンドの実行可否を利用するのが良いと思います。例えば外部コマンド foo が存在しない場合にユーザ定義コマンドを定義したくない場合には以下の様にファイルの先頭に書きます。

if !executable('foo')
  finish
endif

プラグイン固有の設定

上記のリストで、番号が飛んでいるのに気付いたかもしれません。以前は使っていたけれど不要になったので消した為です。これにより、プラグインを足したり消したりする際にゴミが残ってしまう問題を解決しました。

これらの設定で注意する事は、プラグインの有無をチェックする事です。プラグインをチェックする為には、globpath 関数を使ってスクリプトの存在を確認する方法を使います。

例えば vim-lsp の設定ファイル 200-lsp.vim の先頭には以下の様に書いています。

if empty(globpath(&rtp, 'autoload/lsp.vim'))
  finish
endif

こうする事で、vimrc の Plug 行をコメントアウトしても正常に Vim が起動できる様になります。例えば何かの拍子に環境が変わり、プラグインが正常に動作しなくなった場合には、そのプラグインを読み込みを停止してしまえば合わせて設定も無効になってくれる、という仕組みです。

おわりに

以上が、次第に汚くなりがちな vimrc を綺麗に保つために僕が行き着いた方法です。個人的な趣味ですので、皆さんには合わないかもしれませんが、幾らかは盗んで頂ける物があるかもしれません。
Posted at by



2019/11/30


以前、mruby から TensorFlow Lite を扱う為の mrbgems、「mruby-tflite」を書きました。

Big Sky :: MRuby の TensorFlow Lite バインディングを書いた。

以前 TensorFlow Lite の Go バインディングを書いたのだけど Big Sky :: TensorFlow Lite の Go binding を書いた。 Google launche...

https://mattn.kaoriya.net/software/lang/c/20190417102631.htm

KaoriYa さんに Coral EdgeTPU をプレゼントして頂いたので、mruby-tflite を Coral EdgeTPU 対応する為の mrbgems を書きました。

GitHub - mattn/mruby-tflite-edgetpu
https://github.com/mattn/mruby-tflite-edgetpu

元の mruby-tflite とはソースを分離してあります。build_config.rb に以下の様に追加して頂くだけで EdgeTPU を扱える様になります。

conf.gem :github => 'mattn/mruby-tflite'
conf.gem :github => 'mattn/mruby-tflite-edgetpu'
conf.gem :github => 'kjunichi/mruby-webcam'
conf.gem :github => 'qtkmz/mruby-gd'

mruby-tflite と mruby-tflite-edgetpu を連携するには以下の様にインタプリタに対してオプション設定する必要があります。

#! ./bin/mruby

# オプションを生成
options = TfLite::InterpreterOptions.new

# Coral EdgeTPU のデバイス一覧から 0 番目を delegate として割り付け
options.add_delegate TfLite::EdgeTPU.new(TfLite::EdgeTPU.devices[0])

model = TfLite::Model.from_file 'mobilenet_ssd_v2_coco_quant_postprocess_edgetpu.tflite'
interpreter = TfLite::Interpreter.new(model, options)
interpreter.allocate_tensors
input = interpreter.input_tensor(0)
output2 = interpreter.output_tensor(1)
output3 = interpreter.output_tensor(2)
wanted_width = input.dim(1)
wanted_height = input.dim(2)
wanted_channel = input.dim(3)
data = Array.new(wanted_height * wanted_width * wanted_channel, 0)
labels = {}
File.read('coco_labels.txt').lines.each do |x|
  kv = x.split('  ')
  labels[kv[0].to_i] = kv[1].strip
end

cam = Webcam.new(ARGV[0]||0)
cam.set_size(wanted_width, wanted_height)
cam.each(true) {|img|
  decoded = GD::Image.new_from_jpeg_data(img)
  (0...wanted_width).each do |x|
    (0...wanted_height).each do |y|
      pixel = decoded.get_pixel(x, y)
      offset = (y * wanted_width + x) * wanted_channel
      data[offset..offset+2] = [decoded.red(pixel), decoded.green(pixel), decoded.blue(pixel)]
    end
  end
  decoded.destroy
  input.data = data
  interpreter.invoke
  result = []
  output3.data.each_with_index do |v, i|
    next if v < 0.6
    result.push([v, output2.data[i]])
  end
  result.sort{|a, b| b[0] <=> a[0] }.take(5).each_with_index do |v, i|
    s = v[0]
    i = v[1]
    puts "#{labels[i]} #{i} #{s}"
  end 
  puts "---"
}

現状、mruby には OpenCV の様に加工した MAT を表示できる mrbgems が存在しない為、kjunichi さんが作っておられる mruby-webcam で画像を取り込み、qtkmz さんの mruby-gd で画像を解析し、そこに何が映っているのかを端末に出力しているだけですが、今後 OpenCV の様な mrbgems が登場すれば mruby でもリアルタイムで画像を解析し、認識したオブジェクトに枠や名称を描画する事も出来る様になるかもしれません。

mruby-tflite-edgetpu
詳解ディープラーニング 第2版 ~TensorFlow/Keras・PyTorchによる時系列データ処理~ (Compass Booksシリーズ) 詳解ディープラーニング 第2版 ~TensorFlow/Keras・PyTorchによる時系列データ処理~ (Compass Booksシリーズ)
巣籠 悠輔
マイナビ出版 / ¥ 3,740 (2019-11-27)
 
発送可能時間:在庫あり。

Posted at by



2019/11/12


先日、Gopls の v0.2.0 がリリースされました。

v0.2.0
https://github.com/golang/go/issues/33030#issuecomment-549629508

リリースノートに書かれていますが、このバージョンから completeUnimported に対応しています。fmt が import されていなくても fmt.Println が補完できる様になります。ただしデフォルトでは無効になっています。Visual Studio Code であれば以下を settings.json に含める事で使える様になります。

"gopls": {
    "completeUnimported": true
},
vscode

また vim-lsp をお使いであれば以下の様に設定する事で使える様になります。

if executable('gopls')
  augroup LspGo
    au!
    autocmd User lsp_setup call lsp#register_server({
        \ 'name''go-lang',
        \ 'cmd'{server_info->['gopls']},
        \ 'whitelist': ['go'],
        \ 'workspace_config'{'gopls'{
        \     'staticcheck': v:true,
        \     'completeUnimported': v:true,
        \     'caseSensitiveCompletion': v:true,
        \     'usePlaceholders': v:true,
        \     'completionDocumentation': v:true,
        \     'watchFileChanges': v:true,
        \     'hoverKind''SingleLine',
        \   }},
        \ })
    autocmd FileType go setlocal omnifunc=lsp#complete
    autocmd FileType go nmap <buffer> gd <plug>(lsp-definition)
    autocmd FileType go nmap <buffer> ,n <plug>(lsp-next-error)
    autocmd FileType go nmap <buffer> ,p <plug>(lsp-previous-error)
  augroup END
endif

僕はその他にも実験的なオプションを沢山有効にしています。詳しくは gopls の settings.md を参照下さい。

settings.md
https://github.com/golang/tools/blob/master/gopls/doc/settings.md

これらは Visual Studio Code の gopls セクションでも使えますし、同様に vim-lsp の workspace_config でも使えます。

Posted at by