Vim script でメニューを追加&日本語化する際のノウハウ

restart.vimopen-browser.vim で 日本語化されたメニューを提供しているのですが、メニューは無効化していたり使わない人も多く、あまりノウハウが Web 上に無いのでここに記載しておきます。

目次

  1. メニューを追加するタイミング
  2. メニューを追加するかどうかのフラグ
  3. メニューを日本語化したい
  4. メニューを選んだ際に実行される Vim script の式とモード
  5. メニューにセパレーターを追加したい
  6. メニューを任意の位置に配置したい
  7. 実際のコード例
  8. オチ

1. メニューを追加するタイミング

plugin/*.vim の GUIEnter でメニューを追加する処理を行っています。 理由は以下の通りです。

  • メニューは CUI では使われない機能のため
    • GUIEnter は GUI 版でしか発生しないイベント
    • デフォルトの menu ($VIMRUNTIME/menu.vim) も CUI の場合はメニューを追加しないようになっている

2. メニューを追加するかどうかのフラグ

restart.vim では g:restart_no_default_menus という変数が (.vimrc で定義される等して) すでに存在し、かつ0であれば読み込むようになっています。
(この変数はメニューを「追加させない」ための変数なので、0以外 (真) であればメニューを読み込みません)

しかし、そのデフォルト値をどうすべきかについてはよく考える必要があります。
というか実際によく考えずに読み込むようにしていたら thinca さんから
set guioptions+=M してメニューを無効化しているのにrestart.vim のメニューだけ出てきてしまう」
膝に矢報告を受けてしまいました

なので私見ですが、デフォルト値は
「'guioptions' オプションに "M" が含まれていたら1、そうでなければ0」
が良いと思われます。 Vim script の式で書くとこうでしょうか。

echo (&guioptions =~# 'M')

以下のいずれかの場合にメニューを読み込まないようにするには、 次のような Vim script になります。

  • g:restart_no_default_menus という変数が存在しない
  • あるいは値が0である
  • あるいは 'guioptions' オプションに "M" が含まれる場合
if !get(g:, 'restart_no_default_menus', (&guioptions =~# 'M'))
    " File.Restart を選ぶと「:Restart<CR>」を実行する
    anoremenu File.Restart<Tab>:Restart :Restart<CR>
endif

get({dict}, {key} [, {default}]) は {dict} に {key} があればそれを返し、なかったら {default} を返します。

3. メニューを日本語化したい

:menutrans を使用します。 これも thinca さんに教えてもらった機能で、Vim ではデフォルトでメニューの翻訳機能を Vim script から使用することができます。 例として $VIMRUNTIME/menu_ja_jp*.vim を参考にしました。KoRoN++

restart.vim

  1. g:restart_menu_lang (存在するならこれ)
  2. 'langmenu' オプション (空文字じゃないならこれ)
  3. v:lang (上記にマッチしないならこれ)

を順番に見て、それぞれ ja で始まるならメニューを日本語化しています。 具体的には次のようなコードです。

if get(g:, 'restart_menu_lang', &langmenu !=# '' ? &langmenu : v:lang) =~# '^ja'
    runtime! lang/restart_menu_ja.vim
endif

条件文で ja で始まるかどうかを判定し、真であれば 'runtimepath' オプション中の (lang ディレクトリに入れるのが定石のようなので) lang/restart_menu_ja.vim を読み込むようにしています。 また、ファイルを分ける場合はデフォルトのメニューの翻訳 ($VIMRUNTIME/lang/menu_*.vim) と被らないようなファイル名が好ましいと思われます。 lang/restart_menu_ja.vim の中身は以下の通りです。

if exists("did_restart_menu_trans")
    finish
endif
let did_restart_menu_trans = 1
let s:save_cpo = &cpo
set cpo&vim

scriptencoding utf-8
menutrans &Restart<Tab>:Restart  再起動(&R)<Tab>:Restart

let &cpo = s:save_cpo
unlet s:save_cpo

:menutrans の引数の &Restart<Tab>:Restart の部分は :anoremenu の第1引数から親のメニューの指定を取ったものと同じである必要があります。
(ちなみに <silent> 等も指定できるので、必ずしも第1引数になるとは限りません)

anoremenu File.Restart<Tab>:Restart :Restart<CR>

4. メニューを選んだ際に実行される Vim script の式とモード

今まで平然と :anoremenu を使ってきましたが、 メニューを追加するにはマッピングと同じくモードを指定できます。
しかし、:anoremenu を使えば概ねうまく動くメニューを作成してくれます。

例: >

   :amenu File.Next    :next^M

は以下と同じである: >

   :nmenu File.Next    :next^M
   :vmenu File.Next            ^C:next^M^\^G
   :imenu File.Next         ^\^O:next^M
   :cmenu File.Next        ^C:next^M^\^G
   :omenu File.Next        ^C:next^M^\^G

ただし open-browser.vim では

  • 実行しているキーマッピングはノーマルモードとビジュアルモードでのみ提供している
    • :amenu はインサートモード等他の余計なモードについても定義してしまう
  • :amenu ではビジュアルモードのマッピングに余計なマッピングが付加されてしまう

という理由から :amenu は使用していません。

詳しくは :help :amenu を参照してください。

5. メニューにセパレーターを追加したい

- で始めて - で終わる項目を定義すれば良いです。

anoremenu File.-RestartSep- <Nop>

詳しくは :help menu-separator を参照してください。

6. メニューを任意の位置に配置したい

それぞれのメニュー項目には優先度と言うものが設定されていて、 :amenu 等のExコマンドの引数に指定することができます。

anoremenu 10.601 File.Restart<Tab>:Restart :Restart<CR>
anoremenu 10.602 File.-RestartSep- <Nop>

私はちょうどデフォルトメニューの以下画像の位置に追加したかったので上記のような優先度にしました。
デフォルトメニューの優先度については $VIMRUNTIME/menu.vim を参照してください。
(ちなみに File.Restart<Tab>:Restart のように <Tab> で分けると、以下の画像のように右端に何のマッピングを実行するかの簡単な説明 (<Tab> の右に来る文字列) を表示することができます)

f:id:tyru:20160220135051p:plain

詳しくは :help menu-priority を参照してください。

7. 実際のコード例

8. オチ

thinca: こんだけ言っておいてアレだけど、私メニューは一切使っていない…

vim-jp – Lingr