ALEでgometalinterを使う

# tl;dr
[![https://gyazo.com/81a3557563de001f9cc1a23d9ee4a1b5](https://i.gyazo.com/81a3557563de001f9cc1a23d9ee4a1b5.png)](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

```shell-session
go get -u gopkg.in/alecthomas/gometalinter.v1
gometalinter --install --update
```

それぞれのlinterについては以下のような記事を参考にされたし。
- [Go の CI で Lint と カバレッジ回して非人間的なレビューは自動化しよう in 2016年](http://haya14busa.com/ci-for-go-in-end-of-2016/)
- [Go Meta Linter がサポートするツールまとめ
](http://tech.sideci.com/entry/2017/01/10/110000)

# ALE

[ALE](https://github.com/w0rp/ale)は、Vim8もしくはNeoVimで非同期にlinterを実行してくれるプラギンである。
Go以外にも様々な言語[^1]に対応している。
似たようなプラギンは多数存在するわけだが、設定なしでもすぐ使えるという点がよい。
なにも考えなければインストールするだけで使える、便利。
[^1]: https://github.com/w0rp/ale#supported-languagesを参照

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

plugin managerには[dein.vim](https://github.com/Shougo/dein.vim)を使っているのでその場合だと以下ような感じになる。

```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パッチのインストールは以下。

```shell-session
brew tap sanemat/font
brew install ricty --powerline --vim-powerline
```

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

```vim: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](https://github.com/ryanoasis/nerd-fonts)のパッチを以下の手順でRictyにあてあげる必要がある。

```shell-session
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でも飲むとよい <span class="fa fa-spin">:coffee:</span>

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

# lightline

statuslineには[lightline.vim](https://github.com/itchyny/lightline.vim)を使う。
またlightlineのcolorthemeに[tender.vim](https://github.com/jacoborus/tender.vim)を使い、NerdFontを表示するために[vim-devicons](https://github.com/ryanoasis/vim-devicons)をいれる。

プラギンの追加のところに以下を追記。
ちなみにvim-deviconsを先に読み込む必要がある。

```vim:init.vim
call dein#add('ryanoasis/vim-devicons')
call dein#add('itchyny/lightline.vim')
call dein#add('jacoborus/tender.vim')
```

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

```vim: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
```

以上で完了。