vim-jp / vimdoc-ja / tips

tips - Vim日本語ドキュメント

メインヘルプファイルに戻る English | 日本語 | 編集
tips.txt      For Vim バージョン 9.1.  Last change: 2023 Aug 10


                  VIMリファレンスマニュアル       by Bram Moolenaar


Vim を使うときのヒントとアイデア                                tips

これは、多くのユーザーにとって便利だろうと思うことの一部です。wiki には
もっと多くの tips があります。http://www.vim.org

ユーザーマニュアルも参照してください。ユーザーマニュアルにはたくさんのtipsが含
まれていますusr_toc.txt

C のプログラムを編集する                        C-editing
識別子が使われている場所を検索する              ident-search
xterm でのスクリーンの切り替え                  xterm-screens
挿入モードでスクロールさせる                    scroll-insert
スムーズにスクロールさせる                      scroll-smooth
ありがちなタイプミスを修正する                  type-mistakes
単語や行の数を数える                            count-items
カーソルの位置を戻す                            restore-position
ファイルをリネームする                          rename-files
複数ファイル中の名前を置換する                  change-name
外部コマンドの実行速度を速くする                speed-up
便利なマッピング                                useful-mappings
ヘルプファイルを圧縮する                        gzip-helpfile
ウィンドウでシェルコマンドを実行する            shell-window
バイナリ編集                                    hex-editing
自動コマンドで <> 表記を使う                    autocmd-<>
対応する括弧を強調する                          match-parens
カレントウィンドウでヘルプを開く                help-curwin

==============================================================================
C のプログラムを編集する                                C-editing

Vim には C のプログラムファイルを編集するのを手助けする機能がたくさんあります。
詳細をジャンプして確認できるようにタグつきで機能のあらましを紹介しましょう。

usr_29.txt            ユーザーマニュアルの「プログラム内の移動」
usr_30.txt            ユーザーマニュアルの「プログラムの編集」
C-indenting           テキストをタイプ中、自動的に行をインデントします。
=                     数行をインデントし直します。
format-comments       コメントを自動的にフォーマットします。

:checkpath            再帰的にインクルードされるファイルをすべて表示します。
[i                    カーソルの下にある単語と同じものを現在のファイルとイン
                        クルードされるファイルから探します。
[_CTRL-I              "[i" でマッチした場所にジャンプします。
[I                    カーソルの下にある単語と同じものを現在のファイルとイン
                        クルードされるファイルから探し出しその行のリストを表示
                        します。

[d                    カーソルの下の単語の定義を現在のファイルとインクルード
                        されるファイルから探します
CTRL-]                カーソルの下のタグにジャンプします。(例、関数の定義)
CTRL-T                CTRL-]コマンドの前に戻ります。
:tselect              マッチしたタグのリストから一つ選びます。

gd                    カーソルの下のローカル変数の宣言にジャンプする。
gD                    カーソルの下のグローバル変数の宣言にジャンプする。

gf                    カーソルの下のファイル名にジャンプする。

%                     対応する(),{},[],/* */, #if, #else, #endifに移動する。
[/                    前のコメントが始まる場所に移動する。
]/                    次のコメントが終わる場所に移動する。
[#                    前の閉じられていない #if, #ifdef, #else に移動する。
]#                    次の閉じられていない #else, #endif に移動する。
[(                    前の閉じられていない '(' に移動する。
])                    次の閉じられていない ')' に移動する。
[{                    前の閉じられていない '{' に移動する。
]}                    次の閉じられていない '}' に移動する。

v_ab                  「ブロック("[("から"])"まで)」を括弧を含めて選択する。
v_ib                  「ブロック("[("から"])"まで)の内部」を選択する。
v_aB                  「ブロック("[{"から"]}"まで)」を括弧を含めて選択する。
v_iB                  「ブロック("[{"から"]}"まで)の内部」を選択する。

==============================================================================
識別子が使われている場所を検索する                      ident-search

tagsが関数や変数の定義された場所へのジャンプに使えることはすでにわかりました
が、ときどき関数や変数が使われている場所にジャンプしたいことがあります。これ
は二つの方法で実現することができます:
1. :grepを使う方法。この方法はほとんどの Unix システムでうまく動作しますが遅
   いですし(全てのファイルを読むため)、一つのディレクトリの中だけしか検索でき
   ません。
2. ID ユーティリティを使う方法。この方法は速いですし複数のディレクトリを検索で
   きます。この方法は位置を記憶するためにデータベースを使用します。いくつかの
   プログラムを新たに入れる必要があります。そして、データベースを最新に保つこ
   とが要求されます。

GNU id-tools と Vim を連動させる。

必要なもの:
- The GNU id-toolsがインストールされていること(IDを生成するにはmkidが必要でマ
  クロを使うのにlidが必要です)。
- 現在のディレクトリに "ID" と呼ばれる識別子データベースファイルがあること。
  シェルコマンド "mkid file1 file2 .." で作成可能です。

次の行を .vimrc に追加してください。 >
        map _u :call ID_search()<Bar>execute "/\\<" .. g:word .. "\\>"<CR>
        map _n :n<Bar>execute "/\\<" .. g:word .. "\\>"<CR>

        function! ID_search()
          let g:word = expand("<cword>")
          let x = system("lid --key=none " .. g:word)
          let x = substitute(x, "\n", " ", "g")
          execute "next " .. x
        endfun

これを使うには、単語上にカーソルを置いて、"_u" とタイプすればvimは単語を含んだ
ファイルを読みこみます。同じファイルで次に単語が出てくるところを検索するには
"n" をタイプします。"_n" で次のファイルに行きます。

この方法は id-utils-3.2 を使って動作を確認しました (id-utlis-3.2 は id-tools
アーカイブの名前です。近くの gnu-ftp-mirror から入手してください。)

[このアイデアはAndreas Kutscheraさんから頂いたものです]

==============================================================================
xterm でのスクリーンの切り替え          xterm-screens xterm-save-screen

(comp.editors で質問に答えて Juergen Weigert 氏が投稿したもの)

:> もう一つの質問は vim を終了させた後にスクリーンがそのまま残っている
:> ことに関してです。例えばいままで見ていた(編集していた)内容がスクリ
:> ーンに残っていますよね。そして前に行った ls などのコマンドの出力が
:> 消えてしまうのです(例えばスクロールバッファからなくなってしまう)。
:> vim や他の vi 風のエディタでもどうにかして前の画面を復元する方法が
:> あるとは思うのですが、どのようにすれば実現できるのかわかりません。
:> 教えていただけると嬉しいです。それでは。
:
:どなたか詳しい方が答えて下さると思います。vim も vi も xterm の設定を
:みて同じことをそれぞれやっていると思うのですが。

これらは必ずしも同じ動作をするとは限りません、というのはこれは termcap と
terminfo の問題であるかもしれないからです。個々のタイプのターミナルの属性を記
述するデータベースには termcap と terminfoという二つのデータベースがあるという
ことを知っておいてください。エントリが異なっていて、かつ質問にあったプログラム
のどれかが termcap を使っていて他のものが terminfo を使っていた場合、違う動作
をすることになりえます。
(+terminfo も参照してください)

この問題の場合、^[[?47h と ^[[?47l というコントロールシーケンスが答えになりま
す。これらはxtermのメインスクリーンバッファともう一方のバッファを切り替えるの
に用いられます。簡単に動作させてみるには次のコマンドを打ってみてください、 >
        echo -n "^[[?47h"; vim ... ; echo -n "^[[?47l"
あなたが望む動作はこれかもしれません。 (ここで ^[ は ESC キャラクターを意味し
ます。このあとデータベースではこの記号の代わりに \E を使っていることがわかるで
しょう)

起動時に vim は termcap の ti 変数 (terminfo の場合: smcup) の値を echo し、終
了時には te 変数 (terminfo の場合: rmcup) の値をターミナルに echo します。とい
うわけで、これらの二つの変数が上記のコントロールシーケンスを設定する正しい場所
です。

xterm の termcap エントリ (/etc/termcap にあります) と xterm の terminfo エン
トリ (infocmp -C xterm として入手します) とを比べてみてください。双方に次のよ
うなエントリが含まれている必要があります。 >
        :te=\E[2J\E[?47l\E8:ti=\E7\E[?47h:

追伸: もしなんらかの違いを見つけた場合、だれかが(システム管理者かな?) termcap
      と terminfo データベースを継続的にチェックするようにした方がよいです。

注1: feature.h で定義される FEAT_XTERM_SAVE をつけて Vim を再コンパイルすると
内蔵の xterm は上記の "te" と "ti" エントリを含むようになります。

注2: スクリーンのスイッチを行わないようにし、termcap の変更も行いたくない場合
次の行を .vimrc に加えてください。 >
        :set t_ti= t_te=

==============================================================================
挿入モードでスクロールを行う                            scroll-insert

挿入モードでスクリーンの外の部分を見たい場合、CTRL-X CTRL-E と CTRL-X CTRL-Y
を使うことでスクリーンをスクロールさせることができます。
                                                i_CTRL-X_CTRL-E

これを簡単に行うには次のマッピングをすることができます: >
        :inoremap <C-E> <C-X><C-E>
        :inoremap <C-Y> <C-X><C-Y>
(文字通り入力するには、'<' フラグが 'cpoptions' にないことを確認してください)
しかしこれを行うとカーソルの上/下の行のテキストをコピーする機能が使えなくなり
ます。
i_CTRL-E.

カーソルの周りの前後を常に見渡せるように 'scrolloff' オプションの値を大きい値
に設定するのもよいです。'scrolloff' の値がウィンドウの高さの半分以上に設定さ
れているとカーソルが常にスクリーンの中央にあることになりテキストはカーソルの
上下移動にあわせてスクロールすることになります。

==============================================================================
スムーズにスクロールさせる                              scroll-smooth

もう少しスムーズにスクロールさせたい場合、次のマッピングを使うこともできます:
>
        :map <C-U> <C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y>
        :map <C-D> <C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E>

(文字通り入力するには、'<' フラグが 'cpoptions' にないことを確認してください)

==============================================================================
ありがちなタイプミスを修正する                          type-mistakes

しょっちゅう間違ってタイプする単語がいくつかあれば、短縮入力を修正するのに利用
しましょう。例えば: >
        :ab teh the
        :ab fro for

==============================================================================
単語や行などの数を数える                                count-items

カレントバッファの中になんらかのパターンがどのくらいの回数現れるのかを数えるに
は、substituteコマンドを使い、実際に置換するのを避けるためにフラグ 'n' をつけ
ます。報告される数がそのアイテムの数です。例: >

        :%s/./&/gn              文字
        :%s/\i\+/&/gn           単語
        :%s/^//n                行
        :%s/the/&/gn            "the" (どこかしら)
        :%s/\<the\>/&/gn        "the" (単語一致)

'hlsearch' をリセットするか、":nohlsearch" をしたくなるかもしれません。
マッチが1個もないときにエラーになってほしくなければフラグ 'e' をつけます。

別の方法としては、ビジュアルモードでv_g_CTRL-Gを使います。

複数のファイルから検索したければ:vimgrepを使います。

                                                        count-bytes
バイト数を数えるにはこれを使ってください。

        文字列を選択してください(ブロック選択も使えます)
        "y" でコピーしてください
        そして、strlen()を使います >
                :echo strlen(@")
改行は1バイトとして数えられます。

==============================================================================
カーソルの位置を戻す                                    restore-position

ときにはファイルのどこかに変更を加えて、テキストをスクロールすることなくカーソ
ルを元の位置に戻すようなマッピングを書きたくなることがあります。ファイルに書い
た日付を変更する例をあげます: >
   :map <F2> msHmtgg/Last [cC]hange:\s*/e+1<CR>"_D"=strftime("%Y %b %d")<CR>p'tzt`s

位置を記憶する:
        ms      's' マークにカーソルの位置を記録します
        H       ウィンドウに表示された最初の行に移動します
        mt      その行を 't' マークに記録します

位置を元に戻す:
        't      先程のウィンドウに表示された最初の行に移動します
        zt      その行がウィンドウの最初に表示されるようにスクロールします
        `s      最初の位置に戻ります

より高度なことについては winsaveview() と winrestview() を参照してくださ
い。

==============================================================================
ファイルをリネームする                                  rename-files

例えば以下のようなファイルを含むディレクトリがあるとします(ディレクトリはラン
ダムに選び出したものです :-):

buffer.c
charset.c
digraph.c
...

そして *.c を *.bla にリネームしたい場合次のようなコマンドを実行します: >

        $ vim
        :r !ls *.c
        :%s/\(.*\).c/mv & \1.bla
        :w !sh
        :q!

==============================================================================
複数ファイル中の名前を置換する                          change-name

スクリプトファイルを使って複数ファイル中の名前を置換する例です:
        
        以下のように置換コマンドと :update コマンドを含む "subs.vim" というファ
        イルを作成します: >
                :%s/Jones/Smith/g
                :%s/Allen/Peter/g
                :update
<
        Vim で置換したい全ファイルを開き、各引数に対してこのスクリプトを実行
        します: >

                vim *.let
                argdo source subs.vim

:argdo も参照。

==============================================================================
外部コマンドの実行をスピードアップする                  speed-up

いくつかの状況では外部コマンドの実行速度が非常に遅くなる場合があります。これは
Unix でのワイルドカード展開が行われた場合もそうです。いくつかのスピードアップ
する方法を紹介しましょう。

もし .cshrc (もしくは他のファイル、使っているシェルによります)が非常に長いなら
ばそれを対話的に使う部分とそうでない用途(セカンダリーシェルとよく呼ばれます)の
部分にわけるべきです。Vim から ":!ls" のようなコマンドを実行する場合、対話的に
行う必要はありませんよね(例えば、プロンプトを出させたり)。次の行の後にそれらを
おくようにしてください。 >

        if ($?prompt == 0) then
                exit 0
        endif

もう一つの方法は 'shell' オプションに "-f" フラグをつける方法です、例えば: >

        :set shell=csh\ -f

(オプションの中にスペースを含めるにはバックスラッシュが必要です)
こうすると csh が .cshrc ファイルを読み込まないようにすることができます。しか
しこうすることでなんらかが動作しない場合もあるかもしれません。

==============================================================================
便利なマッピング                                        useful-mappings

ここでいくつか好んで使われるマッピングを紹介します。

                                                        map-backtick  >
        :map ' `
シングル引用符の動作をバック引用符の動作のようにします。カーソルをマークがある
行の最初の非空白文字に移動させるのではなく、マークがある桁位置に移動させます。

                                                        emacs-keys
コマンドラインでのキーバインドを Emacs スタイルにします: >
        " 行頭へ移動
        :cnoremap <C-A>         <Home>
        " 一文字戻る
        :cnoremap <C-B>         <Left>
        " カーソルの下の文字を削除
        :cnoremap <C-D>         <Del>
        " 行末へ移動
        :cnoremap <C-E>         <End>
        " 一文字進む
        :cnoremap <C-F>         <Right>
        " コマンドライン履歴を一つ進む
        :cnoremap <C-N>         <Down>
        " コマンドライン履歴を一つ戻る
        :cnoremap <C-P>         <Up>
        " 前の単語へ移動
        :cnoremap <Esc><C-B>    <S-Left>
        " 次の単語へ移動
        :cnoremap <Esc><C-F>    <S-Right>

NOTE: これを利用するには 'cpoptions' から '<' フラグを外しておく必要がありま
す。<>

                                                        format-bullet-list
このマッピングはブレットリストのフォーマットを整えるものです。それぞれのリスト
のエントリの前後に空行がある必要があります。式が使われているのはマッピングを部
分的に解説するコメントを入れるためです。 >

        :let m =     ":map _f  :set ai<CR>"   " 'autoindent' をセット
        :let m ..= "{O<Esc>"                  " アイテムの上に空行を挿入
        :let m ..= "}{)^W"                    " ブレットの後のテキストに移動
        :let m ..= "i      <CR>     <Esc>"    " インデントの為のスペースを挿入
        :let m ..= "gq}"                      " ブレットの後のテキストを整形
        :let m ..= "{dd"                      " 空行を削除
        :let m ..= "5lDJ"                     " ブレットの後にテキストを置く
        :execute m                            |" マッピングを決定する

(<> 表記法 <>。これらは文字どおりにタイプするということに注意してください。
^W は "^" "W" で CTRL-W ではありません。'cpoptions' に '<' がなければ Vim にコ
ピー/ペーストすることができますよ。)

最後のコメントは |" で始まっていることに注意してください、これは ":execute" コ
マンドが直接のコメントを受け付けないからです。

また 'textwidth' を 0 以外の値にしておく必要があります。例えば: >
        set tw=70

最初の行からインデントすることになりますが次のようなマッピングでも同じことがで
きます (Note: このマッピングは多くのスペースを含む長い一つの行です): >
        :map _f :set ai<CR>}{a                                                          <Esc>WWmmkD`mi<CR><Esc>kkddpJgq}'mJO<Esc>j
<
                                                        collapse
これら2つのマッピングは空行(;b)もしくは空白文字のみからなる行(;n)の連続を1行に
します。 >
     :map ;b   GoZ<Esc>:g/^$/.,/./-j<CR>Gdd
     :map ;n   GoZ<Esc>:g/^[ <Tab>]*$/.,/[^ <Tab>]/-j<CR>Gdd

==============================================================================
ヘルプファイルを圧縮する                                gzip-helpfile

ディスクスペースに空きがあまりない人はヘルプファイルを圧縮することができます。
圧縮した後でもヘルプファイルを閲覧することはできます。ヘルプファイルへのアクセ
スは少し遅くなります。また "gzip" プログラムが必要です。

(1) すべてのヘルプファイルを圧縮するには: "gzip doc/*.txt"

(2) "doc/tags" を編集して ".txt" を ".txt.gz" にします。 >
        :%s=\(\t.*\.txt\)\t=\1.gz\t=

(3) 次の行を vimrc に追加します: >
        set helpfile={dirname}/help.txt/gz

{dirname}はヘルプファイルのある場所です。gzipプラグインがファイルの解凍をし
ます。それともし圧縮された "doc" ディレクトリと同じ場所に他のVim ファイルがな
い場合、$VIMRUNTIME が他の Vim ファイルがある場所になるようにしておかなければ
なりません。$VIMRUNTIMEも参照してください。

==============================================================================
ウィンドウでシェルコマンドを実行する                    shell-window

terminal を参照してください。

別の解決策は、端末の画面またはディスプレイウィンドウを "splitvt" プログラムで
分割することです。
このプログラムはたぶんどこかの ftp サーバーで見つけることができると思います。
このプログラムに詳しいのは Sam Lantinga <slouken@cs.ucdavis.edu> です。

他の方法は "window" コマンドを利用する方法で、このコマンドは BSD Unix システ
ムにあります。このコマンドは複数のウィンドウをオーバーラップさせることができま
す。それか "screen" プログラムを使うかです。これは www.uni-erlangen.de で見つ
けることができ、このプログラムでウィンドウの積み重ねをすることができます。

==============================================================================
バイナリ編集                                    hex-editing using-xxd

ユーザーマニュアルの23.4を参照してください。

もしそのファイルがバイナリファイル特有の拡張子(exe、binなど)をしているときは、
あなたの<.vimrc>に次の自動コマンドを加えることで、変換の過程を自動化しておくこ
ともできます。"*.bin" をあなたの編集したいファイルの拡張子の、コンマで区切られ
たリストに変えてください: >

        " vim -b : xxd 形式を使用してバイナリを編集する!
        augroup Binary
          autocmd!
          autocmd BufReadPre  *.bin set binary
          autocmd BufReadPost *.bin
            \ if &binary
            \ |   execute "silent %!xxd -c 32"
            \ |   set filetype=xxd
            \ |   redraw
            \ | endif
          autocmd BufWritePre *.bin
            \ if &binary
            \ |   let s:view = winsaveview()
            \ |   execute "silent %!xxd -r -c 32"
            \ | endif
          autocmd BufWritePost *.bin
            \ if &binary
            \ |   execute "silent %!xxd -c 32"
            \ |   set nomodified
            \ |   call winrestview(s:view)
            \ |   redraw
            \ | endif
        augroup END

==============================================================================
自動コマンドで <> 表記を使う                            autocmd-<>

<> 表記は :autocmd の引数として使っても正しく解釈されません。特殊な文字を入力
するのを避けるには、<> 表記をするための自己破壊的なマッピングを行ってそれから
自動コマンドの中でそのマッピングを呼び出すことで実現することもできます。例:

                                                map-self-destroy  >
 " この方法はファイル名を自動的にメニューリストに追加するものです。
 " 自己破壊的なマッピングを使っています!
 " 1. ファイル名に含まれる 'dots' を \. に変更するためにバッファの一つの行を使
 "    います。
 " 2. それをレジスタ '"' に格納します。
 " 3. その名前をバッファメニューリストに追加します。
 " 警告: この方法にはいくらか副作用があります。現在のレジスタの内容を上書きし
 " たり "i" コマンドへのマッピングをすべて削除してしまったりします。
 "
 autocmd BufNewFile,BufReadPre * nmap i :nunmap i<CR>O<C-R>%<Esc>:.g/\./s/\./\\./g<CR>0"9y$u:menu Buffers.<C-R>9 :buffer <C-R>%<C-V><CR><CR>
 autocmd BufNewFile,BufReadPre * normal i

もう一つのよりよい方法は ":execute" コマンドを使う方法です。文字列の中ではバッ
クスラッシュを前置することで <> 表記が使えます。それまであったバックスラッシュ
は \\ という風に2回重ねるのを忘れないでください。また '"' の前にもバックスラッ
シュを前置しなければなりません。
>
 autocmd BufNewFile,BufReadPre * exe "normal O\<C-R>%\<Esc>:.g/\\./s/\\./\\\\./g\<CR>0\"9y$u:menu Buffers.\<C-R>9 :buffer \<C-R>%\<C-V>\<CR>\<CR>"

実際のバッファメニューではユーザー定義関数を使うべきですが(:functionを参照)、
そうすると<>表記は使いませんので、ここでの例としてそれを使うのは不適切です。

==============================================================================
対応する括弧を強調する                                          match-parens

この例はいくつかの高度なトリックを紹介しています。
- 自動コマンドイベントCursorMovedの使い方
searchpairpos()を使って対応する括弧を見つける方法
synID()を使ってカーソルが文字列やコメントの中にあるかを判定する方法
:matchを使って何かを強調する方法
patternを使ってファイルの特定の位置にマッチさせる方法

これはスクリプトローカル変数を使っているので、Vim script ファイル中に書かなけ
ればなりません。カーソルが文字列やコメントの中にないならば、文字列やコメントは
スキップして検索します。構文強調が有効になっている必要があります。

これより若干高度なバージョンがプラグインmatchparenの中で使われています。
>
        let s:paren_hl_on = 0
        function s:Highlight_Matching_Paren()
          if s:paren_hl_on
            match none
            let s:paren_hl_on = 0
          endif

          let c_lnum = line('.')
          let c_col = col('.')

          let c = getline(c_lnum)[c_col - 1]
          let plist = split(&matchpairs, ':\|,')
          let i = index(plist, c)
          if i < 0
            return
          endif
          if i % 2 == 0
            let s_flags = 'nW'
            let c2 = plist[i + 1]
          else
            let s_flags = 'nbW'
            let c2 = c
            let c = plist[i - 1]
          endif
          if c == '['
            let c = '\['
            let c2 = '\]'
          endif
          let s_skip ='synIDattr(synID(line("."), col("."), 0), "name") ' ..
                \ '=~?  "string\\|comment"'
          execute 'if' s_skip '| let s_skip = 0 | endif'

          let [m_lnum, m_col] = searchpairpos(c, '', c2, s_flags, s_skip)

          if m_lnum > 0 && m_lnum >= line('w0') && m_lnum <= line('w$')
            exe 'match Search /\(\%' .. c_lnum .. 'l\%' .. c_col ..
                  \ 'c\)\|\(\%' .. m_lnum .. 'l\%' .. m_col .. 'c\)/'
            let s:paren_hl_on = 1
          endif
        endfunction

        autocmd CursorMoved,CursorMovedI * call s:Highlight_Matching_Paren()
        autocmd InsertEnter * match none
<

==============================================================================
カレントウィンドウでヘルプを開く                                help-curwin

デフォルトでは、ヘルプは分割したウィンドウに表示されます。カレントウィンドウで
開くのが好みであれば、このカスタム `:HelpCurwin` コマンドを試してください:
>
        command -bar -nargs=? -complete=help HelpCurwin execute s:HelpCurwin(<q-args>)
        let s:did_open_help = v:false

        function s:HelpCurwin(subject) abort
          let mods = 'silent noautocmd keepalt'
          if !s:did_open_help
            execute mods .. ' help'
            execute mods .. ' helpclose'
            let s:did_open_help = v:true
          endif
          if !getcompletion(a:subject, 'help')->empty()
            execute mods .. ' edit ' .. &helpfile
            set buftype=help
          endif
          return 'help ' .. a:subject
        endfunction
<


 vim:tw=78:ts=8:noet:ft=help:norl: