はじめに
AtCoderのAPG4bをサイト上でポチポチ進めていたのですが、補完効かなかったりカーソル移動が貧弱だったので、なるべくサイトと同じようなインターフェースにしつつ、ナイスなエディタ機能を使いたいな〜と思ってここ数日一生環境構築をしていました。
C++初めて触る人の参考になればいいなと思っています。
目標とするのは、
- APG4bのインターフェースと可能な限り同じ形でかける
- かつブラウザ上でコーディングするより賢く(補完や改行、高速なカーソル移動、検索機能)コードがかける
- 標準入力を叩き込んだら高速で結果が出る
の3つです。APG4b終わったばっかマンが書いたんだな〜と思ってもらうとよいかもしれないです(もっとナイスな設定があったら教えてください)。
ちなみに最終的にこんな感じの画面ができます。
左上がソースコード、右上がその実行結果、下が対応する標準入力を入れる部分になっています。
本文
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だとコードの最初のおまじない部分(#include
とusing namespace
)をコピペすることが推奨されていますが、面倒なので自動でやってもらいたいです。
そこで、特定のディレクトリで.cpp
ファイルを開いたときには、おまじないが入った状態からコーディングが始まってもらうようにしました。
まず適当なディレクトリにテンプレートを作りましょう。例えば、nvim ~/.config/nvim/templates/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
に、
autocmd BufNewFile ~/CompetitiveProgramming/AtCoder/*.cpp :0r ~/.config/nvim/templates/atcoder.cpp
などと書きます。これで、~/CompetitiveProgramming/AtCoder
直下で新しく.cpp
ファイルを開いたとき、さっき書いたテンプレートが自動で読み込まれてくれます。うれし〜😊
オートで画面を分割し、上半分にソースコード、下半分に標準入力のフォームを作る
APG4bではソースコードの入力画面と標準入力の画面が上下に分かれてますね。それも自動でやってもらいましょう。
例えば.bashrc
や.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
と打つと次のような画面が出ます。
ちゃんと画面が分かれて、上半分がソースコードのテンプレート、下半分が標準入力用の空ファイルになってくれていますね。
プラグインを入れる
ここまででガワはそれっぽくなったので、肝心のエディタ機能を追加します。
vimのプラグインはdeinというプラグインで管理すると便利です。
これに関する記述は先人が多数作ってくださっているので、細かいところはそちらに任せますが、次の内容をinit.vim
に書き込み、init.vim
と同じ階層にdein.toml
とdein_lazy.toml
を作成して次のように書き込むと概ねうまくいくと思います。
それぞれのプラグインが何してくれているかはざっくり書いておきましたが、かいつまんでいうと次のことができるようになります。
- 補完が効くようになる
- vim上でC++が動かせる
- エラーチェックをしてくれる
- ステータスバーがイケてる感じになる
だいぶモダンなエディタ感が出てきました。うれし〜😊
" 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
[[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
# 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
を自動生成したので、こいつを勝手に読んでくれる感じです。
結果こんな感じになります。おつかれさまでした。
その他細々したこと
#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を開いている。
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で動かそうとすると出力が表示されない問題に永久に悩まされています助けてください