それvimrcでよくね？

## 多くのプラグイン
便利なものがとても多いですが、割とソース量的に大したことないものも多い。
汎用的な部分をなくせば、vimrcでよくね？というものがある。

あと個人的にプラグインなしが好きなので、「それvimrcでよくね？」を紹介していきます。

私のvimrcレポ

https://github.com/serna37/vim

## vim-airline
https://github.com/vim-airline/vim-airline

スマートタブラインやアイコンなどは除きますが、デフォルトで十分にステータスラインを整備できます。
```.vim
" status line
set ruler
set laststatus=2
let ff_table = {'dos' : 'CRLF', 'unix' : 'LF', 'mac' : 'CR'}
fu! SetStatusLine()
  hi User1 cterm=bold ctermfg=7 ctermbg=4 gui=bold guibg=#70a040 guifg=#ffffff
  hi User2 cterm=bold ctermfg=2 ctermbg=0 gui=bold guibg=#4070a0 guifg=#ffffff
  hi User3 cterm=bold ctermbg=5 ctermfg=0
  hi User4 cterm=bold ctermfg=7 ctermbg=56 gui=bold guibg=#a0b0c0 guifg=black
  hi User5 cterm=bold ctermfg=7 ctermbg=5 gui=bold guibg=#0070e0 guifg=#ffffff
  let dict = {'i': '1* INSERT', 'n': '2* NORMAL', 'R': '3* REPLACE', 'c': '4* COMMAND', 't': '4* TERMIAL', 'v': '5* VISUAL', 'V': '5* VISUAL', "\<C-v>": '5* VISUAL'}
  let mode = match(keys(dict), mode()) != -1 ? dict[mode()] : '5* SP'
  retu '%' . mode . ' %* %<%F%m%r%h%w%=%2* %p%% %l/%L %02v [%{&fenc!=""?&fenc:&enc}][%{ff_table[&ff]}] %*'
endf
set statusline=%!SetStatusLine()
```

## fzf
https://github.com/junegunn/fzf.vim

無理だろと思うかも知れませんが、できます。matchfuzzyやpopupがvim組み込みになったことでかなりいい感じに再現できます
別で記事も書いてますのでぜひ
[100行でfzf](https://qiita.com/neras_1215/items/5f58e10d3ba3b846b51b)
```ソース.vim
nnoremap <leader>f :call FzfStart()<CR>

let g:fzf_find_cmd = 'find . -type f -name "*" -not -path "*.git/*" -not -path "*.class"'
let g:fzf_searched_dir = execute('pwd')[1:] " first char is ^@, so trim
let g:fzf_find_result_tmp = []

fu! FzfStart()
  " clear map to escape
  nmapclear
  if stridx(execute('pwd')[1:], g:fzf_searched_dir) == -1 || len(g:fzf_find_result_tmp) == 0
    let g:fzf_find_result_tmp = []
    let g:fzf_searched_dir = execute('pwd')[1:]
    echo 'find files in ['.g:fzf_searched_dir.'] and chache ...'
    cal job_start(g:fzf_find_cmd, {'out_cb': function('s:fzf_find_start'), 'close_cb': function('s:fzf_find_end')})
  endif
  let g:fzf_mode = 'his'
  let g:fzf_searching_zone = '(*^-^) BUF & MRU'
  let g:fzf_pwd_prefix = 'pwd:[' . execute('pwd')[1:] . ']>>'

  let g:fzf_enter_keyword = []
  let g:fzf_his_result = map(split(execute('ls'), '\n'), { i,v -> split(filter(split(v, ' '), { i,v -> v != '' })[2], '"')[0] }) + map(split(execute('oldfiles'), '\n'), { i,v -> split(v, ': ')[1] })
  let g:fzf_find_result = g:fzf_his_result
  let g:fzf_enter_win = popup_create(g:fzf_pwd_prefix, #{ title: 'Type or <BS> / past:<Space> / MRU<>FZF:<Tab> / choose:<Enter> / end:<Esc> / chache refresh:<C-f>',  border: [], zindex: 99, minwidth: &columns/2, maxwidth: &columns/2, maxheight: 1, line: &columns/4-&columns/24, filter: function('s:fzf_refresh_result') })
  cal s:fzf_create_choose_win()
endf

fu! s:fzf_create_choose_win()
  let g:fzf_c_idx = 0
  let g:fzf_choose_win = popup_menu(g:fzf_find_result, #{ title: g:fzf_searching_zone, border: [], zindex: 98, minwidth: &columns/2, maxwidth: &columns/2, minheight: 2, maxheight: &lines/2, filter: function('s:fzf_choose') })
endf

fu! s:fzf_find_start(ch, msg) abort
  let g:fzf_find_result_tmp = add(g:fzf_find_result_tmp, a:msg)
endf

fu! s:fzf_find_end(ch) abort
  echo 'find files in ['.g:fzf_searched_dir.'] and chache is complete!!'
endf

fu! s:fzf_refresh_result(winid, key) abort
  if a:key is# "\<Esc>"
    call popup_close(g:fzf_enter_win)
    call popup_close(g:fzf_choose_win)
    cal s:my_key_map()
    return 1
  elseif a:key is# "\<CR>"
    call popup_close(g:fzf_enter_win)
    cal s:my_key_map()
    return 1
  elseif a:key is# "\<C-f>"
    let g:fzf_searched_dir = execute('pwd')[1:]
    echo 'find files in ['.g:fzf_searched_dir.'] and chache ...'
    cal job_start(g:fzf_find_cmd, {'out_cb': function('s:fzf_find_start'), 'close_cb': function('s:fzf_find_end')})
  elseif a:key is# "\<Space>"
    for i in range(0,strlen(@")-1)
      let g:fzf_enter_keyword = add(g:fzf_enter_keyword, strpart(@",i,1))
    endfor
  elseif a:key is# "\<Tab>"
    let g:fzf_mode = g:fzf_mode == 'his' ? 'fzf' : 'his'
    let g:fzf_searching_zone = g:fzf_mode == 'his' ? '(*^-^) BUF & MRU' : '(*^-^) FZF [' . g:fzf_searched_dir . ']'
    cal popup_close(g:fzf_choose_win)
    cal timer_start(0, { -> s:fzf_create_choose_win() })
  elseif a:key is# "\<BS>" && len(g:fzf_enter_keyword) > 0
    unlet g:fzf_enter_keyword[len(g:fzf_enter_keyword)-1]
  elseif a:key is# "\<BS>" && len(g:fzf_enter_keyword) == 0
    " noop
  else
    let g:fzf_enter_keyword = add(g:fzf_enter_keyword, a:key)
  endif

  if g:fzf_mode == 'his'
    let g:fzf_find_result = len(g:fzf_enter_keyword) != 0 ? matchfuzzy(g:fzf_his_result, join(g:fzf_enter_keyword, '')) : g:fzf_his_result
  else
    let g:fzf_find_result = len(g:fzf_enter_keyword) != 0 ? matchfuzzy(g:fzf_find_result_tmp, join(g:fzf_enter_keyword, '')) : g:fzf_find_result_tmp
  endif

  cal setbufline(winbufnr(g:fzf_enter_win), 1, g:fzf_pwd_prefix . join(g:fzf_enter_keyword, ''))
  cal setbufline(winbufnr(g:fzf_choose_win), 1, map(range(1,30), { i,v -> '' }))
  cal setbufline(winbufnr(g:fzf_choose_win), 1, g:fzf_find_result[0:29]) " re view only first 30 files
  return a:key is# "x" || a:key is# "\<Space>" ? 1 : popup_filter_menu(a:winid, a:key)
endf

fu! s:fzf_choose(winid, key) abort
  if a:key is# 'j'
    let g:fzf_c_idx = g:fzf_c_idx == len(g:fzf_find_result)-1 ? len(g:fzf_find_result)-1 : g:fzf_c_idx + 1
  elseif a:key is# 'k'
    let g:fzf_c_idx = g:fzf_c_idx == 0 ? 0 : g:fzf_c_idx - 1
  elseif a:key is# "\<CR>"
    return s:fzf_open(a:winid, 'e', g:fzf_find_result[g:fzf_c_idx])
  elseif a:key is# "\<C-v>"
    return s:fzf_open(a:winid, 'vnew', g:fzf_find_result[g:fzf_c_idx])
  elseif a:key is# "\<C-t>"
    return s:fzf_open(a:winid, 'tabnew', g:fzf_find_result[g:fzf_c_idx])
  endif
  return popup_filter_menu(a:winid, a:key)
endf

fu! s:fzf_open(winid, op, f) abort
  cal popup_close(a:winid)
  exe a:op a:f
  return 1
endf

```

## comfortable-motion
https://github.com/yuttie/comfortable-motion.vim

ぬるぬるスクロールできるやつですね
細かい物理演算でなく、まぁまぁ適当なスパンでよければ以下でできます
チラつくので、スクロール中はカーソルライン/カラムを非表示にするのも入れてます
```.vim
nnoremap <silent><C-u> :cal Scroll(0, 25)<CR>
nnoremap <silent><C-d> :cal Scroll(1, 25)<CR>
nnoremap <silent><C-b> :cal Scroll(0, 10)<CR>
nnoremap <silent><C-f> :cal Scroll(1, 10)<CR>

" scroll ----------------------------------------
fu! Scroll(vector, delta)
  cal CursorToggle()
  let vec = a:vector == 0 ? "\<C-y>" : "\<C-e>"
  let tmp = timer_start(a:delta, { -> feedkeys(vec) }, {'repeat': -1})
  cal timer_start(600, { -> timer_stop(tmp) })
  cal timer_start(600, { -> CursorToggle() })
endf
fu! NormalExe(aa)
  execute "normal! " . a:aa
endf
fu! CursorToggle()
  set cursorcolumn!
  set cursorline!
endf
```

## winresizer
https://github.com/simeji/winresizer

これは何ていうか、windowフォーカス移動とリサイズだけならキーマップのみでできる
ウィンドウの入れ替えは、個人的に使ったことないのでなくていいかなと。。
```.vim
nnoremap <C-h> <C-w>h
nnoremap <C-l> <C-w>l
nnoremap <C-k> <C-w>k
nnoremap <C-j> <C-w>j

nnoremap <Left> 4<C-w><
nnoremap <Right> 4<C-w>>
nnoremap <Up> 4<C-w>-
nnoremap <Down> 4<C-w>+

" :bo terminal ++row=10などで下部にVSCodeっぽくterminalよく開くと思いますが
" コマンドモードからもフォーカルを移動する場合は以下も必要
tnoremap <C-h> <C-w>h
tnoremap <C-l> <C-w>l
tnoremap <C-k> <C-w>k
tnoremap <C-j> <C-w>j
```

## vim-signature
https://github.com/kshenoy/vim-signature

vimはマークが視覚的な要素0なので、さくらエディタのようにしたいと思ってます。
マークリストをポップアップにしたり
マークを適当な文字でできるようにしたいり
次/前のマーク行に移動、を実装しました
```.vim
"nnoremap <Leader>m :marks abcdefghijklmnopqrstuvwxyz<CR>:normal! `
nnoremap <Leader>m :call MarkMenu()<CR>
nnoremap mm :cal Marking()<CR>
nnoremap mj :cal MarkHank("up")<CR>
nnoremap mk :cal MarkHank("down")<CR>

" mark ----------------------------------------
let g:mark_words = 'abcdefghijklmnopqrstuvwxyz'
fu! MarkMenu()
  let markdicarr = []
  let get_marks = ''
  try
    let get_marks = execute('marks ' . g:mark_words)
  catch
    echo 'no marks'
    retu
  endtry
  for v in split(get_marks , '\n')[1:]
    cal add(markdicarr, {'linenum': str2nr(filter(split(v, ' '), { i,v -> v != '' })[1]), 'val': v})
  endfor
  cal sort(markdicarr, { x, y -> x['linenum'] - y['linenum'] })
  let marks_this = map(markdicarr, { i,v -> v['val'] })
  cal popup_menu(marks_this, #{ title: 'choose marks', border: [], zindex: 100, minwidth: &columns/2, maxwidth: &columns/2, minheight: 2, maxheight: &lines/2, filter: function('MarkChoose', [{'idx': 0, 'files': marks_this}]) })
endf

fu! MarkChoose(ctx, winid, key) abort
  if a:key is# 'j' && a:ctx.idx < len(a:ctx.files)-1
    let a:ctx.idx = a:ctx.idx+1
  elseif a:key is# 'k' && a:ctx.idx > 0
    let a:ctx.idx = a:ctx.idx-1
  elseif a:key is# "\<CR>"
    execute('normal!`' . a:ctx.files[a:ctx.idx][1])
  endif
  return popup_filter_menu(a:winid, a:key)
endf

fu! Marking() abort
  try
    let get_marks = execute('marks ' . g:mark_words)
  catch
    execute('mark a')
    cal MarkShow()
    echo 'marked'
    retu
  endtry
  let l:now_marks = []
  let l:warr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
  for row in split(get_marks , '\n')[1:]
    let l:r = filter(split(row, ' '), {i, v -> v != ''})
    if stridx(g:mark_words, r[0]) != -1 && r[1] == line('.')
      execute('delmarks ' . r[0])
      cal MarkShow()
      echo 'delete mark'
      retu
    endif
    let l:now_marks = add(now_marks, r[0])
  endfor
  let l:can_use = filter(warr, {i, v -> stridx(join(now_marks, ''), v) == -1})
  if len(can_use) != 0
    execute('mark ' . can_use[0])
    cal MarkShow()
    echo 'marked'
  else
    echo 'over limit markable char'
  endif
endf

fu! MarkShow() abort
  cal sign_undefine()
  try
    let get_marks = execute('marks ' . g:mark_words)
  catch
    retu
  endtry
  let l:marks = split(get_marks, '\n')
  if len(marks) == 0
    retu
  endif
  cal remove(marks, 0)
  let mark_dict = {}
  let rownums = []
  for row in marks
    let l:r = filter(split(row, ' '), {i, v -> v != ''})
    let mark_dict[r[1]] = r[0]
    let rownums = add(rownums, r[1])
  endfor
  for row in rownums
    exe "sign define " . row . " text=" . mark_dict[row] . " texthl=ErrorMsg"
    exe "sign place " . row . " line=" . row . " name=" . row . " file=" . expand("%:p")
  endfor
endf
aug sig_aus
  au!
  au BufEnter,CmdwinEnter * cal MarkShow()
aug END

fu! MarkHank(vector) abort
  try
    let get_marks = execute('marks ' . g:mark_words)
  catch
    echo 'no marks'
    retu
  endtry
  let l:marks = split(get_marks, '\n')
  cal remove(marks, 0)
  let mark_dict = {}
  let rownums = []
  for row in marks
    let l:r = filter(split(row, ' '), {i, v -> v != ''})
    let mark_dict[r[1]] = r[0]
    let rownums = add(rownums, r[1])
  endfor
  if a:vector == 'up'
    cal sort(rownums, {x, y -> x - y})
  endif
  if a:vector == 'down'
    cal sort(rownums, {x, y -> y - x})
  endif
  for rownum in rownums
    if a:vector == 'up' && rownum > line('.')
      exe "normal! `" . mark_dict[rownum]
      echo index(rownums, rownum) + 1 . "/" . len(rownums)
      retu
    endif
    if a:vector == 'down' && rownum < line('.')
      exe "normal! `" . mark_dict[rownum]
      echo len(rownums) - index(rownums, rownum) . "/" . len(rownums)
      retu
    endif
  endfor
  echo "last mark"
endf
```

## vim-quickhl
ハイライトつけまくると今何検索してるかわからなくなるのでついでにvim-hitspopも。

https://github.com/t9md/vim-quickhl

https://github.com/obcat/vim-hitspop

これも検索した際にハイライトつけるだけなので簡単
ポップアップは適当に実装しました。。ソース量圧縮を重視で。。
```.vim
set shortmess-=S

nnoremap <silent>* *N:cal HiSet()<CR>:cal Hitpop()<CR>
nnoremap <silent># *N:cal HiSet()<CR>:cal Hitpop()<CR>
nnoremap <silent><Leader>q :noh<CR>:cal clearmatches()<CR>:cal popup_clear(g:hitpopid)<CR>

" highlight -----------------------------------
" on word, bright
aug auto_hl_cword
  au!
  au BufEnter,CmdwinEnter * cal HiCwordStart()
aug END
fu! HiCwordStart()
  aug QuickhlCword
    au!
    au! CursorMoved <buffer> cal HiCwordR()
    au! ColorScheme * execute("hi link QuickhlCword CursorLine")
  aug END
  execute("hi link QuickhlCword CursorLine")
endf

fu! HiCwordR()
  silent! 2match none
  exe "2mat QuickhlCword /\\\<". escape(expand('<cword>'), '\/~ .*^[''$') . "\\\>/"
endf

" on search, multi highlight
aug QuickhlManual
  au!
  au! ColorScheme * cal HlIni()
aug END
let g:search_hl= [
    \ "cterm=bold ctermfg=16 ctermbg=153 gui=bold guifg=#ffffff guibg=#0a7383",
    \ "cterm=bold ctermfg=7  ctermbg=1   gui=bold guibg=#a07040 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=2   gui=bold guibg=#4070a0 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=3   gui=bold guibg=#40a070 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=4   gui=bold guibg=#70a040 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=5   gui=bold guibg=#0070e0 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=6   gui=bold guibg=#007020 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=21  gui=bold guibg=#d4a00d guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=22  gui=bold guibg=#06287e guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=45  gui=bold guibg=#5b3674 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=16  gui=bold guibg=#4c8f2f guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=50  gui=bold guibg=#1060a0 guifg=#ffffff",
    \ "cterm=bold ctermfg=7  ctermbg=56  gui=bold guibg=#a0b0c0 guifg=black",
    \ ]
let g:now_hi = 0
fu! HlIni()
  for v in g:search_hl
    exe "hi UserSearchHi" . index(g:search_hl, v) . " " . v
  endfor
endf
cal HlIni()

fu! HiSet() abort
  let cw = expand('<cword>')
  let already = filter(getmatches(), {i, v -> v['pattern'] == cw})
  if len(already) > 0
    cal matchdelete(already[0]['id'])
    retu
  endif
  cal matchadd("UserSearchHi" . g:now_hi, cw)
  let g:now_hi = g:now_hi >= len(g:search_hl)-1 ? 0 : g:now_hi + 1
endf

" hitspop
let g:hitpopid = ''
fu! Hitpop()
  cal popup_clear(g:hitpopid)
  let g:hitpopid = popup_create(expand('<cword>'), #{ border: [], pos: "topleft", line: 1, col: &columns - 15 })
endf
```

## quick-scope
https://github.com/unblevable/quick-scope

fで移動する先をハイライト。まだ右方向だけですが… まぁ0すればいいよね。

本家同様、先頭文字だけでなく、1撃で行ける文字を剪定しています。
どうしても1回で行けない時はセカンダリのハイライトになります。

```.vim
aug qs_colors
  au!
  au ColorScheme * highlight QuickScopePrimary cterm=bold ctermfg=196 ctermbg=0 guifg=#66D9EF guibg=#000000
  au ColorScheme * highlight QuickScopeSecondary ctermfg=161 ctermbg=0 guifg=#66D9EF guibg=#000000
  au CursorMoved * cal HiFLine()
aug END

let g:fmode_flg = 1
fu! FModeToggle(...)
  if a:0 == 1
    let g:fmode_flg = a:1
  else
    let g:fmode_flg = g:fmode_flg == 1 ? 0 : 1
  endif
endf

fu! HiFLine()
  if g:fmode_flg == 0
    retu
  endif
  cal HiReset('QuickScopePrimary')
  cal HiReset('QuickScopeSecondary')
  let line = line('.')
  let now_line = getline('.')
  let target_arr = []
  let target_arr_second = []
  let col = col('.')
  let offset = col('.')
  while offset != -1
    let start = matchstrpos(now_line, '\<.', offset)
    let ashiato = now_line[col:start[1]-1]
    if stridx(ashiato, start[0]) == -1
      cal add(target_arr, [line, start[2]]) " start char col
    elseif start[2] > 0
      let next_char = now_line[start[2]:start[2]]
      cal add(stridx(ashiato, next_char) == -1 ? target_arr : target_arr_second , [line, start[2]+1])
    endif
    let offset = matchstrpos(now_line, '.\>', offset)[2]
  endwhile
  cal matchaddpos("QuickScopePrimary", target_arr, 16)
  cal matchaddpos("QuickScopeSecondary", target_arr_second, 16)
endf
```

## これから
あとよく使われるものって、
lsp系
snippet系
easymotion
だと思います。

さすがにlsp系は、そもそも別でlsをインストールする必要がありますし
スニペットも別途定義ファイルが必要で、cocとかみたいなlsp対応もありますので

完全独立vim単体であとeasymotionを、ミニマムに再現したいなと思ってます。

2022-11-22 quickscopeの再現を追加