VimのPopup windowとwindow-IDについて
Vim 8.1からPopup windowが実装されました。
詳しくは :h popup
を参照して欲しいですがちょっと凝った使い方をしようとし、filterとcallbackを実装しますが結構ノウハウ、具体的にはwindow-IDの理解が必要になってくるのでここでまとめてみようと思います。
window numberとwindow-ID
Popup windowを作成する関数は戻り値としてwindow-IDを返し、また、Popup windowのfilterとcallbackのコールバック関数の第一引数にはwindow-IDが渡ってきます。そのため、Popup windowを扱ううえでwindow-IDの理解が必須になってくるのです。
まず、Vimにはもともとwindow numberというものがありますがこれはwindow-IDとは全く異なるものになります。
window number
window numberは現在Vim画面上に表示されているウィンドウを左上から順にナンバリングした値になります。ここで気を付けたいのはwindow numberは完全に一意な値にならないことです。 例えばtabpageが複数になる場合、window numberはそれぞれのtabpageで左上から順にナンバリングされるため、Vim全体としてwindow numberが重複することになります。
例
tabpage:1 tabpage:2 +-------------+----------------+ +-------------+----------------+ | | | | | | | 1 | 2 | | 1 | 2 | | | | | | | +-------------+----------------+ +-------+-----+------+---------+ | | | | | | | 3 | | 3 | 4 | 5 | | | | | | | +------------------------------+ +-------+------------+---------+
window-ID
window-IDはwindow numberとは違いウィンドウに完全に一意な値を振ったものになります。そのため、ウィンドウを作成する度に新しいwindow-IDが生成されます。
例
tabpage:1 tabpage:2 +-------------+----------------+ +-------------+----------------+ | | | | | | | 1000 | 1002 | | 1023 | 1020 | | | | | | | +-------------+----------------+ +-------+-----+------+---------+ | | | | | | | 1031 | | 1030 | 1004 | 1050 | | | | | | | +------------------------------+ +-------+------------+---------+ ※ 環境よってwindow-IDは異なる場合があります。
window-IDの重要性
window-IDですがそもそもなぜwindow-IDが重要かというとcallbackやfilterのコールバック関数の中では既存の関数やコマンドを使うと Popup windowに対してではなく、カレントウィンドウに対して適用されます。 このため、このコールバック関数内で唯一のPopup windowに関する情報であるwindow-IDはかなり重要で、これから出来ることを知っておく必要があります。
例
let winid = popup_menu(['aaa', 'bbb'], { \ 'filter' : function('s:filter'), \ 'callback' : function('s:callback'), \ }) echo winid " 1001 function! s:filter(id, key) abort " a:idがそのPopup windowのwindow-IDになります。 echo a:id " 1001 " getline()でPopup windowの行を取得したいところですが、実際はカレントウィンドウの行を取得しようとします。 echo getline(2) " Popup windowに行番号を表示するのではなく、カレントウィンドウに行番号を表示しようとします。 set number return popup_filter_menu(a:id, a:key) endfunction function! s:callback(id, key) abort " a:idがそのPopup windowのwindow-IDになります。 echo a:id " 1001 " getline()でPopup windowの行を取得したいところですが、実際はカレントウィンドウの行を取得しようとします。 echo getline(2) " Popup windowに行番号を表示するのではなく、カレントウィンドウに行番号を表示しようとします。 set number endfunction
window-IDから出来ること
Popup windowのcallbackやfilterのコールバック関数内でそのPopup windowに対しての操作が必要になってきます。
で、必要になってくるのがwinbufnr()
とwin_execute()
です。これはwindow-IDからそのウィンドウに表示されているbuffer numberを取得、window-IDに対してコマンドを適用できます。
このbuffer numberはPopup windowに表示されているバッファに対するものになりますので、既存の関数がいろいろ使えます。
" Popup windowのcallbackやfilterのコールバック関数 function! s:callback_or_filter(id, key) abort " winbufnr()でwindow-IDからそのウィンドウに表示されているbuffer numberを取得できます。 let bnr = winbufnr(a:id) " buffer numberがあれば既存のsetbufline()やgetbufline()でバッファ内容設定/取得できます。 call setbufline(bnr, 1, 'xxx') " window-IDのウィンドウに対して、コマンドを実行することができます。 call win_execute(a:id, 'set number') " Popup windowのカーソル行位置を取得しようとline(".")を単純に使いたいところですが、 " そのまま使うとPopup windowに対してではなく、カレントウィンドウに対してのカーソル行位置 " が取得できてしまいますので、以下のような2ステップを踏まないとカーソル行位置は取得できません。 call win_execute(a:id, 'let w:lnum = line(".")') echo getwinvar(a:id, 'lnum', 0) return popup_filter_menu(a:id, a:key) endfunction
その他
もしfilterとcallbackを実装方法がわからなかったら、私は何個かのPopup windowに関するVimプラグインを作成したりしてますのでご参考に。
Vimでポップアップウィンドウが実装されてからかとてもシンプルなVimプラグインを作るのが最近の好み。
— rbtnn (@rbtnn) November 4, 2019
なにも設定させず1コマンドのみみたいなhttps://t.co/79PqcQN6Kshttps://t.co/LYX2GM4ZaOhttps://t.co/9kddpJ2pDghttps://t.co/m0tQhsUfMahttps://t.co/0bfv4XZSiBhttps://t.co/xOsdhNin4q
最新のdotnetコマンドを使う
これは備忘録です。 最新のdotnetコマンドをインストールするには以下の2つのPowerShellコマンドを実行すればいい。
curl -Uri https://dot.net/v1/dotnet-install.ps1 -OutFile dotnet-install.ps1 .\dotnet-install.ps1 -Version coherent -Channel master
そもそも私は dotnet format
を使ってみたかったので、追加で以下のコマンドを実行した。
dotnet.exe tool install -g dotnet-format
リンク
https://docs.microsoft.com/ja-jp/dotnet/core/tools/dotnet-install-script
Webブラウザ1つで試せるMastodon API
以前に私はマストドンのローカルタイムラインを切る取る「mstdn-picker」というものを作った。 これはローカルタイムライン上にある2つのトゥートを指定すると、その間にあるトゥートをパーマリンク形式として保存できるようなもの。 イメージとしてはTwitterの「Together」をイメージしてもらえるとわかりやすい。ただ、「Together」は任意のツイートを自由に選択できるのに対して、「mstdn-picker」は2点間のトゥートという制限がある。任意に選択できるようにしてもよかったが、サーバーサイドのプログラムを構築する面倒くささがあって、結果、クライアントサイドのJava Script/HTML/CSSだけで完結できるようにした。
前置きが長くなりましたが、この「mstdn-picker」を作って得たMastodon APIのことを以降にざっとまとめる。
Mastodon APIのドキュメント
Mastodon APIについて知りたければ、以下のページを見ればほぼすべてわかる。(ほんとこのページだけあれば十分な感じ)
Webブラウザ1つで試せるMastodon API
まぁでもこういう英語のAPIドキュメントを日本人が初見で使いこなすのはちょっとしたなれが必要なので、ここではWebブラウザ1つで試せるMastodon APIを紹介する。 ここで私がいう「Webブラウザ1つで試せる」とは、
- GET送信である
- ユーザー認証が必要ない
- プログラムのコードを書く必要ない
- Webブラウザのアドレスバーに入力するだけでOK
とする。「ユーザー認証が必要ない」というだけあって試せるAPIは少ししかない。上記のMastodon APIのドキュメント内で「Does not require authentication.」を検索するとどれが使えるかわかる。
1) ユーザーアカウントの情報を取得する
マストドンのアカウントそれぞれには一意のID(数値)が割り当てられている。 私のグルドンのアカウントは以下のように「1073」という数字が割り当てられている。
このIDはどうも作成順で連番らしく、IDの「1」はグルドンの管理者であるdrikinさん、「2」は相方のmazzoさんである(わかりやすいw)。 では、このID(数値)を使ってユーザーアカウントの情報を取得しよう。 使用するAPIは以下のものになる。
GET /api/v1/accounts/:id
これをWebブラウザから試すには
https://mstdn.guru/api/v1/accounts/1073
のように「:id」の部分をIDの数値に、そして先頭に該当のマストドンインスタンスのURLをつければOK。
このAPIを叩くと該当のアカウントの情報が得られる。例えば、
- "created_at" : "2017-04-20T03:04:58.820Z" → アカウントを作成した日付
- "followers_count" : 87 → フォワー数
- "following_count" : 22 → フォロー数
- "statuses_count" : 1757 → トゥートした数
など。APIドキュメント上では「toot」と「status」という単語はほぼ同じ意味で使われていることがわかる。
2) インスタンスに登録されているカスタム絵文字を取得する
使用するAPIは以下のものになる。
GET /api/v1/custom_emojis
で
https://mstdn.guru/api/v1/custom_emojis
となる。試すと...
ぱっとみわかりにくいが、よくよくみるとカスタム絵文字それぞれの画像URLが取得できていることがわかる、便利。
3) トゥートの情報を取得する
アカウントに一意のID(数値)が割り振られていたようにトゥートにも一意のID(数値)が割り振られている。これも連番である。
GET /api/v1/statuses/:id
もう説明は割愛するが、「:id」が「1」は
とそのインスタンスの最初のトゥートが得られる。
ちなみにそのURLの後ろに「/favourited_by」をつけると誰がお気に入りしたかも取得できたりする。
Vim本体のソースコードの読みはじめかた(仮)
私はVim本体のソースコードを読み出したり触りだしてから2年ちょい経ったのでここで脳内GCをしたく、まとめてみようかと思います(Vim歴は8年くらい)。 とりあえず、これからここに書くことは現時点(2019/3/24)のソースコードの話でかつ私が理解している中でのまとめなので間違っていたり、古かったりするかもしれないので注意してください。
Vim本体のソースコードの在り処
Vim本体のソースコードは以下のGithubリポジトリにて管理されています。 ブランチはmasterのみで、その他のgithubリポジトリとかに依存してないので 単純にgitでこのリポジトリ1つをクローンすれば、Vim本体をソースコードをビルドしたりできます。
git clone https://github.com/vim/vim
ビルドの仕方はここでは説明しませんが、以下のページをみる良いかと思います。
Vim本体のソースコードの構成
Vim本体のソースコードはsrcフォルダ下で完結されてます。なので、初めはsrcファルダ以外のファイルやフォルダは無視して問題ないでしょう。 さて、肝心のsrcフォルダ下の構成ですが、まず簡単なのはxxdとteeファルダです。 Vimはxxdとteeというコマンドが別に同梱されています。以下のファルダ下にはそれぞれのコマンドのソースコードが含まれています。 逆にいうとVim本体のソースコードはこのフォルダには含まれてません。
- src/xxd
- src/tee
このため、Vim本体のソースコードを読みたい場合にはこのフォルダ内は無視して構わないでしょう。
次に、poフォルダです。これは各言語(プログラミング言語のほうじゃなくて英語とか韓国語とかのほうの言語)に対応するためのコードが含まれているフォルダになります。 確かにVim本体に関わるフォルダですが、そうそう触るようなものでもないのでこれも無視して構わないでしょう。
- src/po
あと、以下のフォルダも初めは無視しちゃって構わないです。この辺のフォルダは私もまだちゃんと存在意義がわかっているわけではないですが、経験則的に触ったり見たりすることはめったにないです。
すると残るフォルダは以下の通りとなります。この3つのフォルダはかなり重要です。とりあえず、Vim本体のソースコードを読みたいやVimを魔改造したいと思ったならこのフォルダだけを読んでいけばなんとかなります。
- src/testdir
- src/proto
- src/
以降、これらのフォルダについてちょっと詳しく説明したいと思います。
src/testdir
このフォルダはVimのテストが含まれているフォルダになります。Vim本体のソースコードとは直接的に関係はないですが、Vimにパッチを投げたりする上で避けては通れないのでちょっと説明します。 まず、Vimのテストは旧形式と新形式の2つのテスト方法があります。なぜ2つあるかというとただ単に新形式に移行しきれてないだけですので、いつかは新形式だけになると思います。
旧形式
旧形式はinファイルというVimに入力したい内容が書かれたファイルとokファイルという期待値が書かれたファイルの2つペアが1つのテストして、 「test{数字}.{拡張子}」というファイル名で保存されています。
例
- src/testdir/test1.in
- src/testdir/test1.ok
新形式
新形式はpureなVim script言語で書かれています。このため、テストファイルも「test_{テスト名}.vim」というファイル名のvimファイルです。
例
- src/testdir/test_options.vim
もし、新形式のテストを追加したいと思ったらどうすればいいでしょうか。ここでもうちょっと詳しく説明したいと思います。
1) Make_all.makを編集する
Make_all.makにはテストするファイルのリストが格納されています。もし、既存のテストを修正したい場合にはこの工程は不要です。
- src/testdir/Make_all.mak
このファイルのなかでNEW_TEST_RES
という定義があり、この定義が新形式のテストのリストとなりますので、もし、「test_hoge.vim」というファイルを新しく追加したなら、
拡張子をresに変えた「test_hoge.res」という行を新しく追加しましょう。
NEW_TESTS_RES = \ test_arabic.res \ test_arglist.res \ ...
2) vimファイルを作成する
というパスにファイルを作成しましょう。 そのファイルの中身には、以下のような形式のVim script関数を定義します。
func Test_hoge1() " テスト処理 endfunc
この関数名が「Test_」で始まる関数が1つテストとして認識しますので、テストしたいだけ関数を定義していくことになります。 「Test_」で始まらない関数が定義できないというわけではなく、ただテストとして認識しないだけですので、別に「Test_」で始まらない関数を定義しても問題ないです。
テストを書く上で注意点としては、以下の点です。
- 「fu」でも「function!」でもなく「func」で定義すること。
- 「endfunc」で定義すること。
- 「abort」や「range」はつけないこと。
- 変更したオプションなどの変数は使用後に初期化しておくこと。
" テストする set ttytype=xxx call assert_report() " 初期化する set ttytype&
- テストしたくない環境は、関数の先頭で即returnしてスキップすること。
func Test_hoge1() if !has('keymap') return endif " テスト処理 endfunc
3) テストしてみる
「test_hoge.vim」を作成したなら、早速テストを実施してみましょう。 以下のようなコマンドを実行すると1つのvimファイルだけをテストできます。
cd src/testdir make test_hoge.res
FAILEDやSEGVという文字が表示されなければテスト成功していると考えて良いでしょう。
src/proto
このフォルダには関数のプロトタイプ宣言が定義しているファイルが拡張子proという形で格納されています。 これらのファイルは以下のファイルからincludeされていますので、もし、あたらしいproファイルを追加したい場合にはこのファイルの更新が必要です。
- src/proto.h
また、拡張子がproなのでgrepする際に検索対象から外さないように気をつけてくださいw。
src/
さて早速、本題のVim本体のソースコードです。とはいっても私はすべて把握しているわけでもないので、把握している範囲で説明したいと思います。
src/feature.h
このファイルにはVim機能(FEATURE)のdefine変数が定義されています。Makefileやconfigure時の引数でVim機能の有効無効が変更できますが、 このファイルであるVim機能が無効なため、結果的に指定したVim機能は無効になるといったロジックが組まれています。
#if defined(FEAT_NORMAL) \ && (defined(FEAT_GUI_GTK) \ || (defined(FEAT_GUI_MOTIF) && defined(HAVE_XM_NOTEBOOK_H)) \ || defined(FEAT_GUI_MAC) \ || (defined(FEAT_GUI_MSWIN) \ && (!defined(_MSC_VER) || _MSC_VER > 1020))) # define FEAT_GUI_TABLINE #endif
src/vim.h
このファイルにはVim本体のソースコードで全般的に使用されるdefine変数やenum型の定数がずらーっと定義されています。 このファイル内でfeature.hもincludeされています。 全般的に使用されるだけあってどれも重要だと思いますが、私が知っているなかでいくつか紹介します。
auto_event, event_T
このenum型はVimのイベントそれぞれのIDとなってます。たとえば、BufEnterイベントはEVENT_BUFENTER
に対応してます。
BufEnterイベントの影響箇所を調べたいときはEVENT_BUFENTER
でgrepしたりするとよいでしょう。
enum auto_event { EVENT_BUFADD = 0, // after adding a buffer to the buffer list EVENT_BUFDELETE, // deleting a buffer from the buffer list EVENT_BUFENTER, // after entering a buffer EVENT_BUFFILEPOST, // after renaming a buffer EVENT_BUFFILEPRE, // before renaming a buffer EVENT_BUFHIDDEN, // just after buffer becomes hidden ... }; typedef enum auto_event event_T;
hlf_T, HL_FLAGS, HL_ATTR
これらはVim組み込みのhighlightのグループについてです。hlf_T
型の値がHL_FLAGS
の値と関連付いています。
たとえば、highlight EndOfBufferはHLF_EOB
と'~'
に関連付いています。VimのソースコードではHL_ATTR
というマクロを使ってhighlightのグループのIDを取得したりしてます。
typedef enum { HLF_8 = 0 /* Meta & special keys listed with ":map", text that is displayed different from what it is */ , HLF_EOB /* after the last line in the buffer */ , HLF_AT /* @ characters at end of screen, characters that don't really exist in the text */ , HLF_D /* directories in CTRL-D listing */ ... } hlf_T; #define HL_FLAGS {'8', '~', '@', 'd', 'e', 'h', 'i', 'l', 'm', 'M', \ 'n', 'N', 'r', 's', 'S', 'c', 't', 'v', 'V', 'w', 'W', \ 'f', 'F', 'A', 'C', 'D', 'T', '-', '>', \ 'B', 'P', 'R', 'L', \ '+', '=', 'x', 'X', '*', '#', '_', '!', '.', 'o', 'q', \ 'z', 'Z' } #define HL_ATTR(n) highlight_attr[(int)(n)]
さて、HL_FLAGS
の値はどこで使われているのかというと、Vimの&highlight
というオプション変数で使用されています。
'highlight' 'hl' string (default (as a single string): "8:SpecialKey,~:EndOfBuffer,@:NonText, d:Directory,e:ErrorMsg,i:IncSearch, l:Search,m:MoreMsg,M:ModeMsg,n:LineNr, N:CursorLineNr,r:Question,s:StatusLine, S:StatusLineNC,c:VertSplit,t:Title, v:Visual,V:VisualNOS,w:WarningMsg, W:WildMenu,f:Folded,F:FoldColumn, A:DiffAdd,C:DiffChange,D:DiffDelete, T:DiffText,>:SignColumn,-:Conceal, B:SpellBad,P:SpellCap,R:SpellRare, L:SpellLocal,+:Pmenu,=:PmenuSel, x:PmenuSbar,X:PmenuThumb,*:TabLine, #:TabLineSel,_:TabLineFill,!:CursorColumn, .:CursorLine,o:ColorColumn,q:QuickFixLine, z:StatusLineTerm,Z:StatusLineTermNC") global {not in Vi} This option can be used to set highlighting mode for various occasions. It is a comma separated list of character pairs. The first character in a pair gives the occasion, the second the mode to use for that occasion. The occasions are:
このため、&highlight
の初期値をoption.c内で以下のように定義していたりします。新しいhighlightを追加した際にはちょっと注意が必要です。
#define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr,N:CursorLineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC"
src/struct.h
このファイルにはVimで全般的に使用する構造体がずらーっと定義されています。
vartype_T, typval_T
struct.h内の構造体はどれも高頻度で登場するんですが、ぱっとわかりやすいのでいうとtypval_T
型です。
typval_T
型はVimの変数の値を保存しておくための構造体です。
もし、タイプがStringの変数を作りたいなら、typval_T.v_type
にVAR_STRING
を設定し、typval_T.vval
にその変数の値を格納します。
タイプがStringの変数ならtypval_T.vval.v_string
に設定するといった感じです。
typedef enum { VAR_UNKNOWN = 0, VAR_NUMBER, // "v_number" is used VAR_STRING, // "v_string" is used VAR_FUNC, // "v_string" is function name VAR_PARTIAL, // "v_partial" is used VAR_LIST, // "v_list" is used VAR_DICT, // "v_dict" is used VAR_FLOAT, // "v_float" is used VAR_SPECIAL, // "v_number" is used VAR_JOB, // "v_job" is used VAR_CHANNEL, // "v_channel" is used VAR_BLOB, // "v_blob" is used } vartype_T; typedef struct { vartype_T v_type; char v_lock; /* see below: VAR_LOCKED, VAR_FIXED */ union { varnumber_T v_number; /* number value */ #ifdef FEAT_FLOAT float_T v_float; /* floating number value */ #endif char_u *v_string; /* string value (can be NULL!) */ list_T *v_list; /* list value (can be NULL!) */ dict_T *v_dict; /* dict value (can be NULL!) */ partial_T *v_partial; /* closure: function with args */ #ifdef FEAT_JOB_CHANNEL job_T *v_job; /* job value (can be NULL!) */ channel_T *v_channel; /* channel value (can be NULL!) */ #endif blob_T *v_blob; /* blob value (can be NULL!) */ } vval; } typval_T;
src/evalfunc.c
このファイルにはVim script関数に対応する関数がすべて定義されています。このため、Vim script関数の挙動がおかしいとか謎を感じたのならこのファイルの対応する関数をみるとよいでしょう。
たとえば、Vim scriptのabs
関数はf_abs
関数に対応しているといった感じです。functions
の定義でこのマッピングが行われています。
static struct fst { char *f_name; /* function name */ char f_min_argc; /* minimal number of arguments */ char f_max_argc; /* maximal number of arguments */ void (*f_func)(typval_T *args, typval_T *rvar); /* implementation of function */ } functions[] = { #ifdef FEAT_FLOAT {"abs", 1, 1, f_abs}, {"acos", 1, 1, f_acos}, /* WJMc */ #endif {"add", 2, 2, f_add}, ... static void f_abs(typval_T *argvars, typval_T *rettv) { ... }
src/version.c
Vimの:intro
と:version
コマンドの2つの実装が定義されています。とてもわかり易く短いので、試しにコードをいじってビルドしてみるなら、とりあえずこのファイルをいじってみるのがオススメ。
src/option.c, src/option.h
この2つのファイルにはVim内のオプション変数が定義されています。options
にはオプションがアルファベット順ですべてリストアップされています。
そのオプション変数に対応する変数がポインタで保存されているといった感じです。以下の例では&aleph
はp_aleph
のポインタを設定していますので、p_aleph
でgrepすると&aleph
の影響箇所を調べることができます。
#ifdef FEAT_RIGHTLEFT EXTERN long p_aleph; /* 'aleph' */ #endif
static struct vimoption options[] = { {"aleph", "al", P_NUM|P_VI_DEF|P_CURSWANT, #ifdef FEAT_RIGHTLEFT (char_u *)&p_aleph, PV_NONE, #else (char_u *)NULL, PV_NONE, #endif { #if defined(MSWIN) && !defined(FEAT_GUI_MSWIN) ...
おわり
screen.cとかも脳内GCしたいのですが、長くなりすぎちゃうので一旦おわりです。 とりあえず、上記のファイルから足がかりにしてVim本体のソースコードを見ていくのがいいんではないでしょうか。
Excelのダイアログボックスの文字やZipファイル内の日本語ファイル名の文字化けを直す方法
Excelのダイアログボックスの文字やZipファイル内の日本語ファイル名の文字化けを直す方法の備忘録です。
私はUS配列のキーボードを使う都合上、ラップトップPCを買う場合もUS配列のため、Windowsシステムの初期設定が英語圏の言語設定になっていたりします。 そのため、Windowsの表示言語を日本語にするのは知っていたんですが、表題の件を解決するにはこれだけではたりないっぽいです。
解決方法はここをみてわかったんですが、どうもWindowsの表示言語の右にある管理用の言語の設定をクリックしてUnicode対応ではないプログラムの言語を日本語に設定しなければならないみたいです。 そしてWindowsを再起動したら無事解決しました。
タブページを理解しよう
この記事はVim その2 Advent Calendar 2018 - Qiitaの25日目の記事です。
さて、みなさんはVimのタブページをちゃんと理解してますでしょうか? 本記事ではVimのタブページについての説明をしたいと思います。
まず理解しておいてもらいたいのが、Vimのタブページはその他のテキストエディタのタブと本質的に違います。 Vimのタブページは主にウィンドウのレイアウトを保持するものに対して、その他のテキストエディタのタブは1つのファイルを保持するものです。
以下の図はこの2つの関係を示したものになります。
見てわかる通り、Vimではある1つのファイルが複数のタブページに存在する可能性があります。 逆にその他のテキストエディタのタブはある1つのファイルが複数のタブに存在する可能性はありません。
この違いはVimのタブページを使いこなすのに重要です。
例えば、Vimのタブページをその他のテキストエディタのタブのように扱いたいと思ったりすることはあると思いますが、これはかなりの苦労しますし、個人的におすすめしません。
Vimのタブページは、1以上のウィンドウを管理し、ウィンドウは1つのバッファを管理します。そして、バッファに1つのファイルが関連づいている感じです。
タブと1つのファイルが密接に関連づいているその他のテキストエディタとはそもそもの仕組みが違いすぎて、同じように扱ったとしても多分あなたはこのタブページとウィンドウとバッファとファイルの管理に一瞬で頭がオーバーフローするでしょう。
あまりまとまりはないですが本記事で言いたいことはこれだけです。要はVimのタブページをその他のテキストエディタのタブと同じように考えて扱わないで!ということです。この違いを理解すればVimのタブページを使う用途がみえてくるんではないでしょうか。 以上、私がちょくちょく開発しているVimの機能TabSidebarのためにタブページユーザーを増やす記事でしたw。
TabSideBarの進捗旅 at 土善旅館
11月23日から26日にかけて三泊四日のVimmer7人で開発合宿を行った。
土善旅館での食事とか様子とかは他のVimmerのブログを見てもらうとして、私はやったことをつらつら書きます。
1日目
今回の目標は、WindowsにてTabSideBarの描写がおかしくなるのを修正するというのが一応目標だった。いつもはMacBookAirでもくもく会などに参加しているが、今回はSurfacePro4のWindowsでの参戦。
Windowsの描写まわりが壊滅状態 · Issue #3 · rbtnn/vim · GitHub
描写がおかしくなる現象は以下の通り。スクロールしようとすると、TabSideBar分のスクロール位置がずれる。
この現象はMacのTerminal上では発生せず、Windowsでのみ発生するのでずっとTabSideBarの進捗は滞っていた。 1日目はVimの描写回りがコーディングしてあるscreen.cとにらめっこしてあっけなく1日目が終了。この日はなにもわからず、進捗0%... orz
2日目
2日目もscreen.cとにらめっこし途方に暮れていた。2日目も終わろうとしていたころ(23時くらい?)、「scroll_region_set
関数のリージョンってなんだ?」とふと思い、調べてみたところ、term.cに定義してあり、とある条件下の時だけ、このscroll_region_set
関数で描写を行っているっぽい。で、この関数を調べてみたらTabSideBarの対応が全くされていなく、対応したら、あっさりWindowsでのTabSideBarの描写が直った。2日目が終了してもう個人的には進捗100%状態w。
3日目
3日目はコマンドラインでCtrl-dしてtabキーを押すとwildmenuの描写位置がおかしい問題。
コマンドラインでCtrl-dしてtabキーで表示がおかしい · Issue #6 · rbtnn/vim · GitHub
まぁこれは、コマンドラインでCtrl-dしてtabキーを押した時もTabSideBarの幅が考慮されたまま描写されていて、比較的簡単だった。wild_menu_showing
という変数がwildmenuの表示状態を保持していて、wildmenuが表示中はTabSideBarの幅を0にしてあっけなく完了。
次にマウス対応。この時のTabSideBarは全くマウス対応してない状態。
マウス対応(%T) · Issue #4 · rbtnn/vim · GitHub
で、マウスのイベントはVimのどこで処理しているかと探しているとnormal.cのdo_mouse
関数でかなりわかりやすいコードでtablineのマウスクリックイベントが処理されている。screen.cとはUI周りの座標計算ばっかなので全く読みやすくない!
tablineのマウスクリックイベント処理をそのままパクって多分TabSideBarのマウスイベントも実装完了。3日目終了。
ただ、TabSideBarにおいて%T
や%X
はどうするか?という問題は残ったまま保留。
4日目
TabSideBarのテストを書くかーと思ったが、Vim本体のテスト実行するmakeのパラメータを忘れている。 なので、めんどくさいことはとりあえずVim scriptで書き書き....
runtest.vimを実行するだけのコマンドが定義してあるVimプラグインを作成して、
:RunTest test_alot.vim
ステーキを食べて4日目終了。
個人的には進捗が出た4日間でした。よかったよかった。