LoginSignup
13
4

More than 3 years have passed since last update.

APG4bから競プロに入門した初心者がスムーズに移行できるコーディング環境をNeovimで構築する

Last updated at Posted at 2019-08-18

はじめに

AtCoderのAPG4bをサイト上でポチポチ進めていたのですが、補完効かなかったりカーソル移動が貧弱だったので、なるべくサイトと同じようなインターフェースにしつつ、ナイスなエディタ機能を使いたいな〜と思ってここ数日一生環境構築をしていました。
C++初めて触る人の参考になればいいなと思っています。

目標とするのは、

  • APG4bのインターフェースと可能な限り同じ形でかける
  • かつブラウザ上でコーディングするより賢く(補完や改行、高速なカーソル移動、検索機能)コードがかける
  • 標準入力を叩き込んだら高速で結果が出る

の3つです。APG4b終わったばっかマンが書いたんだな〜と思ってもらうとよいかもしれないです(もっとナイスな設定があったら教えてください)。
ちなみに最終的にこんな感じの画面ができます。
IMG_2815.jpg

左上がソースコード、右上がその実行結果、下が対応する標準入力を入れる部分になっています。

本文

neovimのインストール

プログラミング初めてでエディタに特にこだわりがないぜ❗️という人は多分VSCodeを使った方が(周りの話を聞く限り)よさそうです。けど、vimmerはかっこいいから…
ここにある設定は概ねvimでも動くと思いますが、一応neovimを使うことを想定しています(ぼくは元々vimを使っていたのですが、これを機にneovimに乗り換えました。自分の使っていた範囲では両者は相互互換です)。
neovimが自分のPCに入っていない場合は、ここを参考に入れます。ぼくはmacユーザーだったので、brew install neovim で入りました。

neovimの設定とプラグインの導入

vimは自分好みにカスタムできるところと独特の操作感が魅力(?)です。
ガンガン設定を自分好みに書き換えガンガン拡張をブチ込みましょう。neovimの設定はinit.vimというファイルに書いていきます。
たしかneovimインストール直後はinit.vimが存在しないので作ります。

mkdir -p ~/.config/nvim # make a directory to save init.vim
cd ~/.config/nvim # move to the directory
nvim init.vim # make init.vim

オートでテンプレートを読み込む

APG4bだとコードの最初のおまじない部分(#includeusing namespace)をコピペすることが推奨されていますが、面倒なので自動でやってもらいたいです。
そこで、特定のディレクトリで.cppファイルを開いたときには、おまじないが入った状態からコーディングが始まってもらうようにしました。
まず適当なディレクトリにテンプレートを作りましょう。例えば、nvim ~/.config/nvim/templates/atcoder.cppを開いて、

atcoder.cpp
// template for atcoder beginners
// when you create a *.cpp file, this template is loaded

#include <bits/stdc++.h>
using namespace std;

int main() {

}

これを書いて保存します。そしてさっき作ったinit.vimに、

init.vim
autocmd BufNewFile ~/CompetitiveProgramming/AtCoder/*.cpp :0r ~/.config/nvim/templates/atcoder.cpp

などと書きます。これで、~/CompetitiveProgramming/AtCoder直下で新しく.cppファイルを開いたとき、さっき書いたテンプレートが自動で読み込まれてくれます。うれし〜😊

オートで画面を分割し、上半分にソースコード、下半分に標準入力のフォームを作る

APG4bではソースコードの入力画面と標準入力の画面が上下に分かれてますね。それも自動でやってもらいましょう。
例えば.bashrc.zshrcに次の処理を書き込みます。

.zshrc
function atcoder () {
  # if there has already been a stdin.txt, delete it and make a new file
  if [ -e stdin.txt ]; then 
    rm stdin.txt
  fi
  touch stdin.txt
  # make a new file and make an window for stdin.txt and broaden the window for the source codes
  nvim $1 -o stdin.txt -c "11 wincmd +" 
}

これで、~/CompetitiveProgramming/AtCoderにてatcoder test.cppと打つとソースコードと標準入力画面が開きます。便利〜🤭
最後の-c "11 wincmd"は自分の環境だとこのぐらいがちょうどいいかな〜という感じなので適宜調整するといいと思います(ソースコードのウィンドウを11行分デカくしています)。
ここまでで~/CompetitiveProgramming/AtCoderにてatcoder test.cppと打つと次のような画面が出ます。
ちゃんと画面が分かれて、上半分がソースコードのテンプレート、下半分が標準入力用の空ファイルになってくれていますね。
スクリーンショット 2019-08-18 14.23.40.png

プラグインを入れる

ここまででガワはそれっぽくなったので、肝心のエディタ機能を追加します。
vimのプラグインはdeinというプラグインで管理すると便利です。
これに関する記述は先人が多数作ってくださっているので、細かいところはそちらに任せますが、次の内容をinit.vimに書き込み、init.vimと同じ階層にdein.tomldein_lazy.tomlを作成して次のように書き込むと概ねうまくいくと思います。
それぞれのプラグインが何してくれているかはざっくり書いておきましたが、かいつまんでいうと次のことができるようになります。

  • 補完が効くようになる
  • vim上でC++が動かせる
  • エラーチェックをしてくれる
  • ステータスバーがイケてる感じになる

だいぶモダンなエディタ感が出てきました。うれし〜😊

init.vim
" dein settings(plugin manager)
let s:dein_dir = expand('~/.cache/dein')
"dein original
let s:dein_repo_dir = s:dein_dir . '/repos/github.com/Shougo/dein.vim'

"if there is not dein, install it
if &runtimepath !~# '/dein.vim'
  if !isdirectory(s:dein_repo_dir)
    execute '!git clone https://github.com/Shougo/dein.vim' s:dein_repo_dir
  endif
  execute 'set runtimepath^=' . fnamemodify(s:dein_repo_dir, ':p')
endif

"start setting
if dein#load_state(s:dein_dir)
  call dein#begin(s:dein_dir)

  "prepare toml files. toml files hold plugins
  let g:rc_dir    = expand('~/.config/nvim')
  let s:toml      = g:rc_dir . '/dein.toml'
  let s:lazy_toml = g:rc_dir . '/dein_lazy.toml'

  "load toml and cache
  call dein#load_toml(s:toml,      {'lazy': 0})
  call dein#load_toml(s:lazy_toml, {'lazy': 1})

  "end installing
  call dein#end()
  call dein#save_state()
endif

"if there are plugins that have not installed yet, install them
if dein#check_install()
  call dein#install()
endif
dein.toml
[[plugins]]
repo = 'Shougo/dein.vim'

# custom status bar
[[plugins]]
repo    = 'vim-airline/vim-airline'
depends = ['vim-airline-themes']

[[plugins]]
repo = 'vim-airline/vim-airline-themes'

# color scheme
[[plugins]]
repo = 'joshdick/onedark.vim'
hook_add = '''
  colorscheme onedark
dein_lazy.toml
# completement
[[plugins]]
repo = "Shougo/deoplete.nvim"
on_i = 1
on_event = "InsertCharPre"
hook_source = """
  let g:deoplete#enable_at_startup = 1
  let g:deoplete#auto_complete_delay = 0
  let g:deoplete#auto_complete_start_length = 1
  let g:deoplete#enable_camel_case = 0
  let g:deoplete#enable_ignore_case = 0
  let g:deoplete#enable_refresh_always = 0
  let g:deoplete#enable_smart_case = 1
  let g:deoplete#file#enable_buffer_path = 1
  let g:deoplete#max_list = 10000
  set completeopt-=preview
"""

[[plugins]]
repo = "zchee/deoplete-clang"
on_ft = ["cpp", "cxx", "cp", "cc"]
depends = "deoplete.nvim"
hook_add = """
  let g:deoplete#sources#clang#libclang_path='/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib'
  let g:deoplete#sources#clang#clang_header='/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang'
"""

# comletement for header files
[[plugins]]
repo = 'Shougo/neoinclude.vim'
on_ft = ["cpp", "cxx", "cp", "cc"]
depends = ['deoplete.nvim']

# error checker
[[plugins]]
repo = 'w0rp/ale'
hook_add = '''
    let g:ale_sign_column_always = 1
    let g:ale_fix_on_save = 1
    let g:ale_completion_enabled = 1
    let g:ale_sign_error = '⨉'
    let g:ale_sign_warning = '⚠'
    let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
    let g:ale_statusline_format = ['⨉ %d', '⚠ %d', '⬥ ok']
    let g:ale_linters = {
        \   'c' : ['clangd'],
        \   'cpp' : ['clangd']
    \}
'''

# quickrun
[[plugins]]
repo = 'thinca/vim-quickrun'
    hook_add = '''
    " keymapping
    nnoremap <Space><Space> :wa <bar> :wincmd <bar> :QuickRun <stdin.txt <CR>

    let g:quickrun_config = {}

    " when you run a cpp file, measure the execution time
    let g:quickrun_config['cpp'] = {
    \  'cmdopt' : '-std=c++14 -Wall',
    \  'hook/time/enable' : 1
    \}

    let g:quickrun_config['_'] = {
    \  'split' : 'vertical'
    \}
    set splitright
'''

quickrunの設定でノーマルモードのダブルスペースを標準入力を読み込んでquickrunにしているのが個人的なお気に入りポイントです。
ダブルスペースを全てのウィンドウの編集内容を保存→左上のウィンドウに移動→標準入力を引き取ってquickrunを実行に割り当てているので、どこのウィンドウにいてもノーマルモードでダブルスペースを叩くとコードを実行してくれます。うれし〜😊
上の方でatcoder hoge.cppコマンドでstdin.txtを自動生成したので、こいつを勝手に読んでくれる感じです。
結果こんな感じになります。おつかれさまでした。
スクリーンショット 2019-08-18 11.57.56.png

その他細々したこと

#include <bits/stdc++.h>

上の方でテンプレートに #include <bits/stdc++.h> と書き込みましたが、ローカル環境だと動きません。
ここを参考にヘッダーファイルを作りましょう。

init.vimのカスタム

ここで紹介した以外にも色々init.vimはいじりようがあります。
例えばぼくは読み込むファイルの種類ごとにインデントの幅を変えたり(pythonは4、C++は2など)、方向キーで移動できるようにしたり、ctrl+a(e)で文頭(末)へ移動できるようにしたりしています。キーバインド(自分の好きなキーに特定の操作を割り当てること)はvimカスタムの醍醐味なので、各自自分にあった素敵バインドを設定してもらえればと思います。
先人の素敵セッティングスは「vimrc おすすめ」とかでググると大量に出てくるので是非参考にしましょう。
(.vimrcは無印vimのinit.vimに当たるものですが、両者とも概ね同じ書き方で同じ機能が実現できます。)

vimでneovimをエイリアスする

vimからneovimに乗り換えた人は特にですが、vimをnvimでエイリアスしましょう。
init.vimを編集したのに設定が適用されてねえ❗️」というお前、お前はvimを開いている。

.zshrc
alias vim='nvim'

ちなみに元々vimを使ってたよ〜という人はinit.vimのなかで.vimrcを読み込むこともできます。
新しく設定ファイル作るのが面倒な場合はそれでも良いと思います。ぼくも最初は.vimrcを読んでいたのですが、NeoBundleからdeinに乗り換えたりして大幅に内容が変わったので、結局新しくinit.vimを作り直しました。

My settings

ここまでの内容のほとんどはぼくのリポジトリに置きました。.zshrcの編集部分以外は全部あると思います。
見た目の綺麗さ優先でhook_addをファイル分けしたりしてますが、基本的に内容は変わらないです。

まとめ

  • 指定したディレクトリ上でatcoder hoge.cppと入力するとAPG4bのインターフェースlikeな画面が展開するようにした
  • ソースコード上のノーマルモードでスペースを2回押すと標準入力を読み込んで結果を画面右上に開くようにした

Future Work

自分の中で整理がついたらぼちぼち更新したり記事追加したりします

  • 答え合わせ機能をつけたい
  • quickrunのダブルスペースキーマップをatcoderディレクトリに限定する(init.vimでdein読んだあと、autocmdで入れると多分できますね)
  • quickrunをvimprocで動かそうとすると出力が表示されない問題に永久に悩まされています助けてください
13
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
4