この記事について
この記事は Vim 駅伝 の 2024/09/02 の記事です。 前回の記事は eetann さんによる、 2024/08/30 の「NeovimでLLMを動かすcodecompanion.nvimの使い方」という記事でした。
次回は 2024/09/04 に投稿される予定です。
背景
「Vim はあるが less はない」という特殊なコンテナ環境で、 「毎回 less インストールするの面倒だな」と思った私は、 Vim をページャーにしようとした。
結論
環境変数 PAGER
に
"sed -r 's/\x1B\[[0-9;]*[mGKH]//g' | vim -R -"
を指定する。
export PAGER="sed -r 's/\x1B\[[0-9;]*[mGKH]//g' | vim -R -"
$ export PAGER="sed -r 's/1B[[0-9;]*[mGKH]//g' | vim -R -"
— 猫 (@h_east) August 25, 2024
なぜかこれだとカラー表示になりますね。理由は分からないです💦
まー、もともと、パイプで渡すとちゃんとカラー表示にはなりますけど、ページャーじゃないとダメなのですよね。。
$ git diff HEAD^ HEAD | vim -R -
調べた・検討した内容(大体時系列)
ページャーにするための基本的な条件
標準入力を受け取り、 Vim をリードオンリーで開く
これもまた特殊なことなのですが、 vim
コマンドはあるが
view
コマンドがないのである。
vim
をリードオンリーモードで開くには -R
オプションを付ける。
標準入力を受け取って Vim のバッファを開くには、引数に -
を指定する。
echo "Hello, World!" | vim -R -
vim -Rですかね?
— Kazuhiro YOSHIKAWA (@yoshikaw) August 25, 2024
vim -R -
をページャーにした結果
制御コードが表示されてぐちゃぐちゃに…
— 大雪 命 (@mikoto2000) August 25, 2024
export PAGER='vim -R -'
してgit diff HEAD^ HEAD
した結果…。かなしい…… pic.twitter.com/FZS9GyvOWo
Vim 側で制御コード消せないですかね?
有用な情報が色々集まる
monaqa(@monaqa.bsky.social)さん、響(@4513echo.dev)さん、猫さん(@h_east)、情報ありがとうございました。
tter-tweet”>プラグインもないことはないものの、だいたいターミナルバッファ使うほうが早くて確実なので自分はそっちを使っちゃいますね
— monaqa (@monaqa.bsky.social) Aug 25, 2024 at 23:54
[image or embed]
export PAGER='vim -R -'
して
git diff HEAD^ HEAD
した結果…。かなしい……
pic.twitter.com/FZS9GyvOWo
このプラグインはどうでしょうか? github.com/powerman/vim…
— 響 (@4513echo.dev) Aug 26, 2024 at 0:02
[image or embed]
うーん。Vimの :s でエスケープシーケンスを消しちゃう?Read onlyじゃなくなっちゃうけど。
— 猫 (@h_east) August 25, 2024
$ export PAGER="vim - +'%s/^[[[0-9;]*[mGKH]//g' +1"
※:sの "^[" は、Ctrl-v してEscキーを押す。
以下のメッセージがうざい場合は –not-a-term オプション付ける。
Vim: Reading from stdin…
それと並行して迷走する私
モダンでシングルバイナリな less コマンドって何がありますかね?(ページャーとして使いたい)
— 大雪 命 (@mikoto2000) August 25, 2024
どうせ Vim 上のターミナルで実行するのだし、
— 大雪 命 (@mikoto2000) August 25, 2024export PAGER="cat"
するのが良いのかもしれない…?
そもそも端末がスクロールできる現在、 pager を使う必要が本当にあるのだろうか???
— 大雪 命 (@mikoto2000) August 25, 2024
いや、普通に出力が残るのは邪魔だわな……
Vim をページャーにできた!
が、色がつかないのが不満
猫さん(@h_east)の方法で表示はぐちゃぐちゃにならなくなったが、画面に色がつかない。
git diff HEAD^ HEAD
:
しかし、あらかじめ色情報を消しておくと色がつく…
git diff --no-color HEAD^ HEAD
:
sed
で制御コードを削除し、パイプで Vim に渡す」までを PAGER
に指定する
ということで、色がつかないと不満をこぼしたら、猫さん(@h_east)が解決策を考案してくださいました。
$ export PAGER="sed -r 's/1B[[0-9;]*[mGKH]//g' | vim -R -"
— 猫 (@h_east) August 25, 2024
なぜかこれだとカラー表示になりますね。理由は分からないです💦
まー、もともと、パイプで渡すとちゃんとカラー表示にはなりますけど、ページャーじゃないとダメなのですよね。。
$ git diff HEAD^ HEAD | vim -R -
これで前述の結論である PAGER
に指定するコマンド
"sed -r 's/\x1B\[[0-9;]*[mGKH]//g' | vim -R -"
が生まれました。
色がついたりつかなかったりする原因の考察と調査
「制御コードアリで色がつかない、制御コードなしで色がつくという事は、 Vim はバッファの内容を見て filetype を判定する機能も持ってるんだなぁ」と思ったので、 どこでどうやっているのかを確認してみた。
Vim ヘルプを見る
「使いたいファイル形式がVimに検出されない(存在しない)場合」の話が記載されており、 その中のパターン D に「ファイル形式がファイルの内容を調べる事によってのみ検出可能な場合」 の記述があった。
より多くの例については$VIMRUNTIME/scripts.vimを参照
new-filetype-scripts - filetype - Vim日本語ドキュメント より
scripts.vim
とあったので、実際に見てみる。
" Vim support file to detect file types in scripts
"
" Maintainer: The Vim Project <https://github.com/vim/vim>
" Last Change: 2023 Aug 27
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" This file is called by an autocommand for every file that has just been
" loaded into a buffer. It checks if the type of file can be recognized by
" the file contents. The autocommand is in $VIMRUNTIME/filetype.vim.
" Bail out when a FileType autocommand has already set the filetype.
if did_filetype()
finish
endif
" Load the user defined scripts file first
" Only do this when the FileType autocommand has not been triggered yet
if exists("myscriptsfile") && filereadable(expand(myscriptsfile))
execute "source " . myscriptsfile
if did_filetype()
finish
endif
endif
" The main code is in a compiled function for speed.
call dist#script#DetectFiletype()
短い。
- 処理の流れとしては、
did_filetype()
でtrue
が帰ってきたら、もうファイルタイプ決定済みなので何もしない。 - その後、
myscriptfile
を読み込めれば読み込み、ファイルタイプ決定したかをまたチェックする。 - 最後に、ここまで来てもファイルタイプが決まっていなければ
dist#script#DetectFiletype()
を実行する。
という感じ。 dist#script#DetectFiletype()
に判定の本体がありそうなのでこれを探す。
見つけた場所は autoload/dist/script.vim
。
autoload/dist/script.vim
の DetectFileType
DetectFileType
関数:
export def DetectFiletype()
var line1 = getline(1)
if line1[0] == '#' && line1[1] == '!'
# File that starts with "#!".
DetectFromHashBang(line1)
else
# File does not start with "#!".
DetectFromText(line1)
endif
enddef
- シェバングで決まればそれで
- それでも決まらなければバッファの内容から判定する
という感じ。
autoload/dist/script.vim
の DetectFromText
DetectFromText
の方も見ていく。
def DetectFromText(line1: string)
var line2 = getline(2)
var line3 = getline(3)
var line4 = getline(4)
var line5 = getline(5)
...(snip)
先頭 5 行目までを読み込み、この後にファイルタイプ判定処理が続いている。
例えば diff
の判定処理は以下。
# Diff file:
# - "diff" in first line (context diff)
# - "Only in " in first line
# - "--- " in first line and "+++ " in second line (unified diff).
# - "*** " in first line and "--- " in second line (context diff).
# - "# It was generated by makepatch " in the second line (makepatch diff).
# - "Index: <filename>" in the first line (CVS file)
# - "=== ", line of "=", "---", "+++ " (SVK diff)
# - "=== ", "--- ", "+++ " (bzr diff, common case)
# - "=== (removed|added|renamed|modified)" (bzr diff, alternative)
# - "# HG changeset patch" in first line (Mercurial export format)
elseif line1 =~ '^\(diff\>\|Only in \|\d\+\(,\d\+\)\=[cda]\d\+\>\|# It was generated by makepatch \|Index:\s\+\f\+\r\=$\|===== \f\+ \d\+\.\d\+ vs edited\|==== //\f\+#\d\+\|# HG changeset patch\)'
|| (line1 =~ '^--- ' && line2 =~ '^+++ ')
|| (line1 =~ '^\* looking for ' && line2 =~ '^\* comparing to ')
|| (line1 =~ '^\*\*\* ' && line2 =~ '^--- ')
|| (line1 =~ '^=== ' && ((line2 =~ '^=\{66\}' && line3 =~ '^--- ' && line4 =~ '^+++') || (line2 =~ '^--- ' && line3 =~ '^+++ ')))
|| (line1 =~ '^=== \(removed\|added\|renamed\|modified\)')
setl ft=diff
読み解くところまではしないが、 line1
を基に判定し、ヒットしたら ft=diff
していることがわかる。
ついでに git
は以下。
# Git output
elseif line1 =~ '^\(commit\|tree\|object\) \x\{40,\}\>\|^tag \S\+$'
setl ft=git
こちらも line1
の内容で判定している。
このような仕組みで、拡張子やシェバングでも判定できないファイルタイプを、バッファの内容を使って判定する処理が行われていた。
じゃぁ
script.vim
はどこからどのタイミングで呼ばれるの?
Vim のソースを grep したところ、 runtime/filetype.vim
から、 autocmd
の BufNewFile,BufRead
で呼び出されているようだ。
" Check for "*" after loading myfiletypefile, so that scripts.vim is only used
" when there are no matching file name extensions.
" Don't do this for compressed files.
augroup filetypedetect
au BufNewFile,BufRead *
\ if !did_filetype() && expand("<amatch>") !~ g:ft_ignore_pat
\ | runtime! scripts.vim | endif
au StdinReadPost * if !did_filetype() | runtime! scripts.vim | endif
なので、「制御コード入りの入力を受け取ってから制御コードを削除」では色がつかず、 「あらかじめ制御コードを削除してから受け取る」では色がつく。という事らしい。
以上。
0 件のコメント:
コメントを投稿