VimでgfしたらURLをブラウザで開く

by
カテゴリ:

Vimアドベントカレンダー12/9の記事です。
昨日の記事は以下の2本でした。

gfはいいぞ

gfコマンド、便利ですよね。
ファイル名前の上でgfと入力すると、そのファイルをバッファに開いてくれます。

この上位互換にgFもあり、file.txt:30 みたくファイル名に行番号が付いている場合に該当業を開いてくれます。
なので、以下のようにgfgFにしてしまうのは定番といえます。

nnoremap gf gF

いきなり脱線しましたが、:help gfを見ると、netrwプラグインを入れてればURIも開けるよとのこと。何それ便利。

If the name is a hypertext link, that looks like “type://machine/path”, you need the |netrw| plugin.

netrwはいらないけど、URLくらいは開きたいので、実装してみました。

マッピングを使う

たぶん、王道です。
カーソル下のURLを見つけて、任意のプログラムで開きます。

これまた :help gf を読んで気付いたのですが、<cfile>という文字列は、カーソル下にあるファイル名に相当するそうです。
従ってexpand('<cfile>')すると、実際のファイル名(URL)に展開されます。

あとは'<cfile>'がURLかどうか判定して動作を切り替えればOK。
Linuxならxdg-openを使うといいでしょう。他は知らん。
Neovim nightly使いなら、vim.ui.open()という関数でよしなにできるっぽいです。

-- Lua (Neovim向け)
vim.keymap.set("n", "gf", function()
  local cfile = vim.fn.expand("<cfile>")
  if cfile:match("^https?://") then
    -- Neovim nightlyなら `vim.ui.open(cfile)` が便利。
    vim.fn.system({"xdg-open", cfile})
  else
    vim.cmd("normal! gF")
  end
end)
" Vim script (ChatGPTに翻訳させたものを手直し)
function! OpenFileOrURL()
  let cfile = expand("<cfile>")
  if match(cfile, '^https\?://') >= 0
    call system('xdg-open ' . cfile)
  else
    normal! gF
  endif
endfunction

nnoremap <silent> gf :call OpenFileOrURL()<CR>

自動コマンドを使う

あるいはこんなものも面白いかもしれません。
特に設定してない状態でURLにカーソルを合わせえてgfすると、URLをファイル名とした空のバッファが開きます。
これを逆手にとって、BufReadCmdイベントで発火する自動コマンドを登録します。

バッファの開閉という副作用がデメリットですが、:e https://example.comなどでブラウザが開くようになるのは便利かも?

やることとしては以下の通り。
Luaスクリプトを用意してますが、Vim scriptが必要な人はChatGPTなりなんなりに問い合わせてください。

  1. ファイル名前がhttps://またはhttp://で始まることを条件とする
  2. 任意のコマンド(ブラウザ)でファイルを開く
  3. 空のバッファがゴミになるので、直前のバッファを開きなおし(e #)、ゴミバッファを消す
    • 直前のバッファを開きなおして、ゴミバッファを非表示にしておくのがポイントで、これにより、バッファを消したせいでウィンドウが閉じるのをふせげます。
vim.api.nvim_create_autocmd("BufReadCmd", {
  desc = "Open browser with gf",
  pattern = { "https://*", "http://*" },
  group = vim.api.nvim_create_augroup("open-browser"),
  callback = function(ctx)
    if vim.ui.open then
      -- Neovim nightlyを使っている場合
      vim.ui.open(ctx.match)
    else
      vim.fn.system({"xdg-open", cfile})
    end
    vim.cmd([[e # | bdelete #]])
  end,
})

ENJOY!

<cfile>を知らず、最初に思いついたのが自動コマンドでした。ヘルプ読むのって面白いですね!