Vim
Go
fontforge
neovim

ALEでgometalinterを使う

More than 1 year has passed since last update.


tl;dr

https://gyazo.com/81a3557563de001f9cc1a23d9ee4a1b5


  • Vim8 or NeoVimからgometalinterを使う

  • linterプラギンとしてALEを使う

  • 画像のような表示にするためにNerd Fontsを使う


gometalinter

Go用のlinterはいくつかあるがこれらを統合的に扱えるようにしたものである。

サポートしているlinterはgithub上に記載されている。

https://github.com/alecthomas/gometalinter#supported-linters

Install

go get -u gopkg.in/alecthomas/gometalinter.v1

gometalinter --install --update

それぞれのlinterについては以下のような記事を参考にされたし。

- Go の CI で Lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年

- Go Meta Linter がサポートするツールまとめ


ALE

ALEは、Vim8もしくはNeoVimで非同期にlinterを実行してくれるプラギンである。

Go以外にも様々な言語1に対応している。

似たようなプラギンは多数存在するわけだが、設定なしでもすぐ使えるという点がよい。

なにも考えなければインストールするだけで使える、便利。

Goでもいくつかのlinterを使えるようになっているが、デフォルトはgofmtになっているのでgometalinterにする必要がある。

plugin managerにはdein.vimを使っているのでその場合だと以下ような感じになる。


init.vim

if &compatible

set nocompatible
endif
let s:dein_dir='~/.cache/dein/repos/github.com/Shougo/dein.vim'
set runtimepath+=s:dein_dir

if dein#load_state('~/.cache/dein')
call dein#begin({)

call dein#add(s:dein_dir)
call dein#add('w0rp/ale')
call dein#end()
call dein#save_state()
endif

filetype plugin indent on
syntax enable

" for ALE
let g:ale_lint_on_text_changed = 'never'
let g:ale_set_loclist = 0
let g:ale_set_quickfix = 1
let g:ale_sign_column_always = 1
let g:ale_lint_on_enter = 0
let g:ale_open_list = 1
let g:ale_keep_list_window_open = 0
" for go
let g:ale_linters.go ='gometalinter'
let g:ale_go_gometalinter_options = '--fast --enable=staticcheck --enable=gosimple --enable=unused'


g:ale_go_gometalinter_optionsでgometalinter実行時のoptionを渡せる。

--fastは実行に時間のかかるlinterを除いて実行してくれる。

上記の設定だとファイル保存時にlintしてくれる。非同期に実行するとはいえ、--fastなしだともっさりしてしまうのでつけている。

それ以外の--enableはお好みで追加。

これだけでALEがgometalinterを実行してくれるようになる。


Powerline

linter結果にPowerlineフォントを使う。

みんな大好きRictyのpowerlineパッチのインストールは以下。

brew tap sanemat/font

brew install ricty --powerline --vim-powerline

terminalのfontをRictyPowelineにしつつ、ALEの設定に以下を追加する。


init.vim

let g:ale_statusline_format = ['⨉ %d', '⚠ %d', '⬥ ok']

let g:ale_sign_error = '⤫'
let g:ale_sign_warning = '⚠'


Nerd Fonts

icon fontをstatuslineに表示するためにNerd Fontsのパッチを以下の手順でRictyにあてあげる必要がある。

brew install fontforge

git clone https://github.com/ryanoasis/nerd-fonts
cd nerd-fonts
chmod +x font-patcher
fontforge -script ./font-patcher ~/Library/Fonts/Ricty\ Regular\ for\ Powerline.ttf --fontawesome --fontlinux --oct
icons --pomicons --powerline --powerlineextra
cp -f ./Ricty\ Regular\ for\ Powerline\ Nerd\ Font\ Plus\ Font\ Awesome\ Plus\ Octicons\ Plus\ Pomicons\ Plus\ Font\ Linux.ttf ~/Library/Fonts/
fc-cache -vf

nerd-fontsのcloneにはそれなりに時間がかかるのでcoffeeでも飲むとよい :coffee:

ここで作ったフォントをiTerm2とかの設定で使うようにする。

ALEの設定に以下を追加。


lightline

statuslineにはlightline.vimを使う。

またlightlineのcolorthemeにtender.vimを使い、NerdFontを表示するためにvim-deviconsをいれる。

プラギンの追加のところに以下を追記。

ちなみにvim-deviconsを先に読み込む必要がある。


init.vim

call dein#add('ryanoasis/vim-devicons')

call dein#add('itchyny/lightline.vim')
call dein#add('jacoborus/tender.vim')

lightline用の設定として以下を追加します。(長文)


init.vim

let g:lightline = {

\ 'colorscheme': 'tender',
\ 'active': {
\ 'left': [ [ 'mode', 'paste' ], [ 'fugitive', 'filename' ] ],
\ 'right': [ [ 'lineinfo' ], ['percent'], [ 'ale_error', 'ale_warning', 'ale_ok', 'char_code', 'fileformat', 'fileencoding', 'filetype' ] ]
\ },
\ 'component_function': {
\ 'fugitive': 'LightLineFugitive',
\ 'filename': 'LightLineFilename',
\ 'fileformat': 'LightLineFileformat',
\ 'filetype': 'LightLineFiletype',
\ 'fileencoding': 'LightLineFileencoding',
\ 'mode': 'LightLineMode',
\ 'char_code': 'LightLineCharCode',
\ },
\ 'component_function_visible_condition': {
\ 'mode': 1,
\ },
\ 'component_expand': {
\ 'ale_error': 'LightLineAleError',
\ 'ale_warning': 'LightLineAleWarning',
\ 'ale_ok': 'LightLineAleOk',
\ },
\ 'component_type': {
\ 'ale_error': 'error',
\ 'ale_waring': 'waring',
\ 'ale_ok': 'ok',
\ },
\ 'separator': {'left': '⮀', 'right': '⮂'},
\ 'subseparator': {'left': '⮁', 'right': '⮃'}
\ }

augroup LightLineOnALE
autocmd!
autocmd User ALELint call lightline#update()
augroup END

function! LightLineModified()
return &ft =~ 'help' ? '' : &modified ? '' : &modifiable ? '' : '-'
endfunction

function! LightLineReadonly()
return &ft !~? 'help' && &readonly ? '' : ''
endfunction

function! LightLineFilepath() abort
let l:path_string = substitute(expand('%:h'), $HOME, '~', '')
if winwidth(0) < 120 && len(l:path_string) > 30
let l:path_string = substitute(l:path_string, '\v([^/])[^/]*%(/)@=', '\1', 'g')
endif
return l:path_string
endfunction

function! LightLineFilename()
let fname = expand('%:t')
return fname == 'ControlP' ? g:lightline.ctrlp_item :
\ fname == '__Tagbar__' ? g:lightline.fname :
\ fname =~ '__Gundo\|NERD_tree' ? '' :
\ &ft == 'vimfiler' ? vimfiler#get_status_string() :
\ &ft == 'denite' ? LightLineDenite() :
\ &ft == 'vimshell' ? vimshell#get_status_string() :
\ ('' != LightLineReadonly() ? LightLineReadonly() . ' ' : '') .
\ ('' != fname ? LightLineFilepath().' '.fname : '[No Name]') .
\ ('' != LightLineModified() ? ' ' . LightLineModified() : '')
endfunction

" ref. https://gist.github.com/pocari/84c78efa38b5c2fc1f659d1aac3face8
function! LightLineDenite()
let mode_str = substitute(denite#get_status_mode(), "-\\| ", "", "g")
call lightline#link(tolower(mode_str[0]))
return mode_str
endfunction

function! LightLineFugitive()
try
if expand('%:t') !~? 'Tagbar\|Gundo\|NERD' && &ft !~? 'vimfiler' && exists('*fugitive#head')
let mark = ' ' " edit here for cool mark
let _ = fugitive#head()
return strlen(_) ? mark._ : ''
endif
catch
endtry
return ''
endfunction

function! LightLineFileformat()
return winwidth(0) > 120 ? &fileformat . (exists('*WebDevIconsGetFileFormatSymbol') ? ' ' . WebDevIconsGetFileFormatSymbol() : '') : ''
endfunction

function! LightLineFiletype()
return winwidth(0) > 70 ? (strlen(&filetype) ? &filetype . (exists('*WebDevIconsGetFileTypeSymbol') ? ' ' . WebDevIconsGetFileTypeSymbol() : ''): 'no ft') : ''
endfunction

function! LightLineFileencoding()
return winwidth(0) > 120 ? (strlen(&fenc) ? &fenc : &enc) : ''
endfunction

function! LightLineMode()
let fname = expand('%:t')
return fname == '__Tagbar__' ? 'Tagbar' :
\ fname == 'ControlP' ? 'CtrlP' :
\ fname == '__Gundo__' ? 'Gundo' :
\ fname == '__Gundo_Preview__' ? 'Gundo Preview' :
\ fname =~ 'NERD_tree' ? 'NERDTree' :
\ &ft == 'unite' ? 'Unite' :
\ &ft == 'denite' ? 'Denite' :
\ &ft == 'vimfiler' ? 'VimFiler' :
\ &ft == 'vimshell' ? 'VimShell' :
\ winwidth(0) > 60 ? lightline#mode() : ''
endfunction

function! LightLineCharCode() abort
if winwidth(0) <= 120
return ''
endif
" if char on cursor is `Λ̊`, :ascii returns below.
" <Λ> 923, 16進数 039b, 8進数 1633 < ̊> 778, 16進数 030a, 8進数 1412
redir => l:tmp | silent! ascii | redir END
let l:chars = []
call substitute(l:tmp, '<.>\s\+\d\+,\s\+\S\+ \x\+,\s\+\S\+ \d\+', '\=add(l:chars, submatch(0))', 'g')
if len(l:chars) == 0
return ''
endif
let l:ascii = []
for l:c in l:chars
let l:m = matchlist(l:c, '<\(.\)>\s\+\d\+,\s\+\S\+ \(\x\+\)')
if len(l:m) > 0
call add(l:ascii, printf('%s %s', l:m[1], l:m[2]))
endif
endfor
return join(l:ascii, ', ')
endfunction

function! LightLineAleError() abort
return s:ale_string(0)
endfunction

function! LightLineAleWarning() abort
return s:ale_string(1)
endfunction

function! LightLineAleOk() abort
return s:ale_string(2)
endfunction

function! s:ale_string(mode)
if !exists('g:ale_buffer_info')
return ''
endif

let l:buffer = bufnr('%')
let l:counts = ale#statusline#Count(l:buffer)
let [l:error_format, l:warning_format, l:no_errors] = g:ale_statusline_format

if a:mode == 0 " Error
let l:errors = l:counts.error + l:counts.style_error
return l:errors ? printf(l:error_format, l:errors) : ''
elseif a:mode == 1 " Warning
let l:warnings = l:counts.warning + l:counts.style_warning
return l:warnings ? printf(l:warning_format, l:warnings) : ''
endif

return l:counts.total == 0? l:no_errors: ''
endfunction


以上で完了。