Vimの折り返し表示と自動改行を止める(wrap / textwidth)
Vimで「折り返し」と言うとき、指しているものは2つあります。長い行を画面の端で見た目だけ次の段に送るwrapと、入力中に本当に改行文字を入れてしまうtextwidthです。前者はファイルの中身を変えませんが、後者は変えます。「勝手に改行される」と困っているのはたいてい後者で、しかもvimrcにset textwidth=0と書いても止まらないことがあります。
止まらない原因は、ファイルタイプ別の設定(ftplugin)があとから値を入れ直すことです。この記事では、表示の折り返しを整える設定を一通り見たうえで、:verboseで入れ直したファイルを突き止め、ファイルタイプごとに止めるところまでを実機の出力で順に追います。
検証環境:Vim 9.1.1752(macOS同梱の/usr/bin/vim)とNVIM v0.12.6(Homebrew)。中身の無い一時ディレクトリをHOMEにしてtmuxの中で起動し、幅40桁などに固定した画面をcapture-paneで取り込みました。掲載している画面と:verboseの出力はすべてその実行結果です。
目次(9項目)
| やりたいこと | 設定・キー |
|---|---|
| 見た目の折り返しをやめる | set nowrap |
| 英単語の途中で折らない | set linebreak |
| 折り返した段を1段ずつ移動 | gj gk |
| 改行を入れた設定元を見る | :verbose set tw? fo? |
| 自動改行を止める | autocmd FileType * setlocal fo-=t fo-=c |
「折り返し」には表示と改行の2種類がある
最初に2つを区別しておくと、以降の設定がどちらに効くのか迷わなくなります。
| 種類 | 決めるオプション | ファイルの中身 | 既定 |
|---|---|---|---|
| 表示の折り返し | wrap linebreak showbreak breakindent | 1行のまま | wrapがオン |
| 自動改行 | textwidth formatoptions | 改行文字が入る | textwidth=0(入らない) |
見分け方は簡単で、折り返された2段目にカーソルを置いて:echo line('.')を打ちます。1段目と同じ行番号が出れば表示の折り返しで、番号が1つ増えていれば実際に改行されています。set numberにしているなら、2段目の行番号の欄が空白かどうかでも分かります。
表示の折り返し wrap と nowrap
wrapは既定でオンになっていて、ウィンドウの幅を超えた行は次の段へ送られます。set nowrapにすると折り返さず、画面からはみ出た部分は見えなくなります。幅40桁の画面で並べると次のとおりです。
wrap(既定)
The quick brown fox jumps over the lazy
dog and keeps running across the field u
ntil sunset.
日本語の文章を長く書いて画面の幅を超えさ
せると、どこで折り返されるかを見るための
nowrap
The quick brown fox jumps over the lazy
日本語の文章を長く書いて画面の幅を超えさ
line 4
wrapのままだと英文がuとntilのように単語の途中で切れます。これは次の節のlinebreakで直せます。
nowrapにしたときは横へのスクロールを自分で行います。zlとzhが1桁ずつ、zLとzHが画面の半分ずつ動かします。zsはカーソルのある桁を画面の左端へ、zeは右端へ寄せます。41桁目にカーソルを置いてzsを押すとdog and keeps running across the field uが左端から始まり、zeを押すとhe quick brown fox jumps over the lazy dのように41桁目のdが右端に来る位置まで戻りました。
カーソルを画面の外へ動かしたときのずれ方はsidescrollで決まります。Vimの既定は0で、40lで画面の端を越えると半画面ぶん一気に飛びました。Neovimの既定は1で、1桁ずつ滑らかに追従します。Vimで同じ動きにしたいならset sidescroll=1を足します。
もう1つ、wrapのときに気になるのが画面の下端です。最後の行が収まりきらないと、Vimの既定(displayが空)ではその行がまるごと@の行に置き換わります。幅40桁・本文5段の画面で、5行目に長い英文がある状態を3通りで見ました。
display=(空)
line 4
@
display=truncate
line 4
@@@
display=lastline
line 4
The quick brown fox jumps over the la@@@
lastlineにすると、入る部分までは表示して末尾にだけ@@@を付けます。Neovimではlastlineが既定でした。Vimでも公式のdefaults.vimはtruncateを入れますが、macOS同梱のVimはシステムのvimrcにlet skip_defaults_vim=1があり、defaults.vimが働かないので空のままです。長い行の多いファイルを読むなら、vimrcにset display=lastlineを書いておくと下端の行が読めます。
単語の切れ目で折る linebreak・showbreak・breakindent と日本語
表示の折り返しの見た目を整えるオプションは3つあります。前の節と同じ幅40桁の画面に、英文の行・日本語の行・字下げした英文の行を1つずつ置き、上から順に1つずつ足していった結果です。showbreakの段はlinebreakも効いた状態、breakindentの段は3つとも効いた状態で、字下げした行だけを抜き出しています。
set linebreak
The quick brown fox jumps over the lazy
dog and keeps running across the field.
日本語の文章を長く書いて画面の幅を超えさ
せると、どこで折り返されるかを見るための
行です。
- indented list item that is long
enough to wrap around the window edge
set showbreak=>>\
The quick brown fox jumps over the lazy
>> dog and keeps running across the
>> field.
日本語の文章を長く書いて画面の幅を超えさ
>> せると、どこで折り返されるかを見るた>
>> めの行です。
set breakindent
- indented list item that is long
>> enough to wrap around the window
>> edge
linebreakを付けると、英文はenとoughのような切れ方をしなくなり、空白の位置で次の段へ送られます。折る位置の候補はbreakatに並んだ文字で、既定値は ^I!@*-+;:,./?(空白・タブと半角記号)でした。VimとNeovimで同じ値です。
日本語の行は、linebreakの有り無しで折れる位置が1文字も変わりませんでした。breakatに全角の読点や句点が入っていないので、和文には折ってよい位置が1つも無いことになり、いつもどおり幅いっぱいで折れます。linebreakで日本語が読みやすくなることは期待できません。
showbreakは2段目以降の先頭に付ける目印です。例では>>と空白を付けました。日本語の行の末尾に>が出ているのは、目印のぶん幅が減って全角1文字が入りきらなくなった桁を埋める表示で、ファイルにその文字があるわけではありません。breakindentは2段目以降を元の行の字下げにそろえるので、箇条書きやコードの長い行が読みやすくなります。linebreakとshowbreakを外してbreakindentだけにしても、字下げはそろいました。
タブや行末の空白のように「見えない文字」を見えるようにする設定はタブ・改行・行末スペースを可視化するlistcharsの担当で、この記事の折り返しの設定とは別に組み合わせて使えます。
折り返した行の中を移動する gj と gk
wrapのままだと、jとkは画面の段ではなくファイルの行を単位に動きます。2段に折り返された1行目の先頭でjを押すとカーソルは2行目の先頭へ飛び、1行目の2段目は素通りします。段単位で動くのがgjとgkです。:echo line('.') col('.')で位置を確かめると次のようになりました。
| 操作 | 結果(行 桁) | 着いた場所 |
|---|---|---|
| 開始 | 1 1 | 1行目の先頭 |
| j | 2 1 | 2行目の先頭 |
| gj | 1 41 | 1行目の2段目(dogの位置) |
| もう1回gj | 2 1 | 2行目の先頭 |
文章を書くことが多いなら、jとkをgjとgkに差し替える設定が定番です。ただし素直にnnoremap j gjとすると、5jのような回数付きの移動まで段単位になり、relativenumberの数字と合わなくなります。回数を付けたときだけ元のjに戻す形にしておくと両立します。
nnoremap <expr> j v:count ? 'j' : 'gj'
nnoremap <expr> k v:count ? 'k' : 'gk'
この設定で、回数なしのjは1 41(2段目)に、2jは3 1(2行下の先頭)に着きました。
入力中に勝手に改行される textwidth と formatoptions
ここからはファイルの中身が変わる側の話です。textwidth(略してtw)に桁数を入れると、挿入モードでその桁を超えたところで、直前の空白の位置に改行が入ります。0なら何もしません。素のVimはtw=0なので、何も設定しなければ自動改行は起きません。
ただしtextwidthは「何桁で折るか」を決めるだけで、「何を折るか」はformatoptions(fo)の文字で決まります。自動改行に関わるのは次の2つです。
t:本文をtextwidthで自動改行するc:コメントだけを自動改行し、次の行にコメント記号を付ける
違いは、Vim scriptのファイル(filetype=vim)で確かめると分かりやすいです。ファイルタイプ別の設定が入った状態で、"から始まるコメント行と、letの行に同じ長い英文を打ちました。
let s:message = "this is a long comment line written to see where vim inserts a newline automatically while typing in insert mode"
let g:x = 1
" this is a long comment line written to see where vim inserts a newline
" automatically while typing in insert mode
コメント行だけが78桁の手前で改行され、次の行の頭に"が自動で付きました。letの行は100桁を超えても1行のままです。このときのformatoptionsはcroqlで、cはあってもtが無いからです。
Gitのコミットメッセージ(filetype=gitcommit)では逆にtが入り、textwidth=72になります。本文に長い1文を打つと、VimでもNeovimでも同じ位置で改行されました。
Fix cache
Fix the cache invalidation so that stale pages are no longer served
after a deploy finishes
vimrcで止めても戻る原因は ftplugin(:verbose で犯人を探す)
ここが本題です。コメントの自動改行を嫌って、vimrcに次のように書いたとします。filetype plugin indent onはファイルタイプ別の設定を読み込むための行で、多くのvimrcに入っています。
filetype plugin indent on
set textwidth=0
set formatoptions-=t formatoptions-=c
この状態で.vimのファイルを開いても、コメントは相変わらず78桁で改行されました。何が値を書き換えたかは:verboseを付けて値を表示すると出てきます。
:verbose set tw? fo?
textwidth=78
Last set from /usr/share/vim/vim91/ftplugin/vim.vim line 135
formatoptions=croql
Last set from /usr/share/vim/vim91/ftplugin/vim.vim line 55
Last set fromのあとが、最後に値を書いたファイルと行番号です。vimrcではなくVim本体に同梱のftplugin/vim.vimが書いていることが分かります。そのファイルの該当行は次のとおりでした。
" Set 'formatoptions' to break comment lines but not other lines,
setlocal fo-=t fo+=croql
" Format comments to be up to 78 characters long
if &tw == 0
setlocal tw=78
endif
順番を追うと理由が分かります。vimrcは起動時に1回だけ読まれ、ftpluginはファイルを開いてファイルタイプが決まったときに読まれます。つまりvimrcのsetはftpluginより先に効いていて、あとから上書きされます。しかもtextwidthのほうは「0なら78にする」という条件なので、vimrcにset textwidth=0と書くことは、78を入れてもらう条件をわざわざ満たしていることになります。
Neovim 0.12.6でも、~/.config/nvim/init.vimに同じ2行を書いて同じ結果になりました。出てくるパスが/opt/homebrew/Cellar/neovim/0.12.6/share/nvim/runtime/ftplugin/vim.vim line 122(foはline 50)に変わるだけです。Neovimはfiletype plugin onが既定なので、init.vimに何も書かなくてもこの状態になります。
一方、macOS同梱のVimをvimrc無しで起動すると、.vimのファイルでもfiletype=が空でtextwidth=0のままでした。前の節で触れたskip_defaults_vimのためfiletype plugin indent onが実行されないからです。「vimrcを足したら急に改行されるようになった」という場合は、vimrcのどこかでファイルタイプ別の設定を有効にしたことが引き金になっています。どのファイルが読まれているかを一覧で見るなら設定ファイルが読み込まれない時に使う:scriptnames、いまのファイルタイプが何になっているかは:set ft?で確かめられます。ファイルタイプの判定そのものを変えたいときはファイルタイプを手動指定する方法を参照してください。
ファイルタイプごとに止める・残す設定例
止めるには、ftpluginより後で値を書けばよいことになります。方法は2つで、どちらもVimとNeovimの両方で効きました。
1つ目はautocmd FileTypeです。ftpluginの読み込みも同じFileTypeのイベントで動くので、filetype plugin indent onより後に書いた自動コマンドのほうが後に実行されます。
filetype plugin indent on
autocmd FileType * setlocal formatoptions-=t formatoptions-=c
このvimrcで:verbose set fo?を見るとformatoptions=roql/Last set from ~/.vimrc line 3に変わり、コメント行に長文を打っても1行のままになりました。ここで2行の順番を入れ替え、autocmdをfiletypeの行より前に書いたところ、formatoptions=croqlに戻って改行も復活しました。自動コマンドは登録された順に実行されるので、先に登録したほうが先に動いてftpluginに上書きされます。効かないときはこの順番を疑ってください。
2つ目はafter/ftpluginに置く方法で、特定のファイルタイプだけを変えたいときに向いています。Vimなら~/.vim/after/ftplugin/vim.vim、Neovimなら~/.config/nvim/after/ftplugin/vim.vimを作り、1行だけ書きます。
setlocal formatoptions-=c
afterの下は同梱のftpluginの後に読まれる場所です。:verbose set fo?の設定元は~/.vim/after/ftplugin/vim.vim line 1になり、コメントの自動改行は止まりました。
どちらの方法でも、textwidthは78のまま残しています。foからtとcを抜けば入力中の改行は起きず、gqで整形したいときだけ78桁が使われるからです。Gitのコミットメッセージのように72桁の慣習を守りたいファイルタイプなら、autocmd FileType gitcommit setlocal formatoptions-=tのようにtだけ抜く選択もあります。実際にこの1行を足すとformatoptions=lnになり、textwidth=72は残ったまま本文は改行されなくなりました。
日本語の段落をgqで整える場合は、formatoptionsにmMが要ります。tw=30にして長い日本語の1行にgqqを打つと、fo=tcqのままでは1行のまま何も起きず、set fo+=mMのあとでは「日本語の文章を長く書いて画面の」「幅を超えさせると、どこで折り返」「されるかを見るための行です。」の3行に分かれました。mが全角文字の間での改行を許し、Mが行をつなぐときに全角文字の間へ空白を入れないようにします。gqとgwの操作そのものは長い行を折り返すVim道場のレッスンで手を動かしながら練習できます。
長いあいだ、vimrcのset textwidth=0は効いていると思い込んでいました。普段開くファイルでは改行されなかったからです。ところがプラグインの設定を書いている最中に、コメントがある長さでぷつりと切れて次の行に"が付き、しかも:set tw?を打つと78が返ってきました。自分では0にしたはずの値が別の数字になっていて、最初はvimrcが読まれていないのだと疑って:scriptnamesを眺めていました。答えは:verboseの1行で出ていて、ずいぶん遠回りをしました。
まとめ
画面の端で見た目だけ折れているのがwrap、ファイルに改行が入るのがtextwidthとformatoptionsです。見た目はnowrap・linebreak・breakindent・display=lastlineで整え、折り返した段の中はgjとgkで動きます。linebreakは日本語には効きません。
自動改行がvimrcで止まらないときは、まず:verbose set tw? fo?を打って設定元を見ます。たいていはftpluginなので、filetype plugin indent onより後のautocmd FileTypeか、after/ftpluginでfoからtとcを抜けば止まります。textwidthは残しておけば、gqで整形するときに使えます。