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プラグインを作成したりしてますのでご参考に。

最新の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

f:id:rbtnn:20190421211605j:plain

リンク

https://docs.microsoft.com/ja-jp/dotnet/core/tools/dotnet-install-script

https://github.com/dotnet/core-sdk

Webブラウザ1つで試せるMastodon API

github.com

以前に私はマストドンのローカルタイムラインを切る取る「mstdn-picker」というものを作った。 これはローカルタイムライン上にある2つのトゥートを指定すると、その間にあるトゥートをパーマリンク形式として保存できるようなもの。 イメージとしてはTwitterの「Together」をイメージしてもらえるとわかりやすい。ただ、「Together」は任意のツイートを自由に選択できるのに対して、「mstdn-picker」は2点間のトゥートという制限がある。任意に選択できるようにしてもよかったが、サーバーサイドのプログラムを構築する面倒くささがあって、結果、クライアントサイドのJava Script/HTML/CSSだけで完結できるようにした。

前置きが長くなりましたが、この「mstdn-picker」を作って得たMastodon APIのことを以降にざっとまとめる。

Mastodon APIのドキュメント

Mastodon APIについて知りたければ、以下のページを見ればほぼすべてわかる。(ほんとこのページだけあれば十分な感じ)

github.com

Webブラウザ1つで試せるMastodon API

まぁでもこういう英語のAPIドキュメントを日本人が初見で使いこなすのはちょっとしたなれが必要なので、ここではWebブラウザ1つで試せるMastodon APIを紹介する。 ここで私がいう「Webブラウザ1つで試せる」とは、

  • GET送信である
  • ユーザー認証が必要ない
  • プログラムのコードを書く必要ない
  • Webブラウザのアドレスバーに入力するだけでOK

とする。「ユーザー認証が必要ない」というだけあって試せるAPIは少ししかない。上記のMastodon APIのドキュメント内で「Does not require authentication.」を検索するとどれが使えるかわかる。

1) ユーザーアカウントの情報を取得する

マストドンのアカウントそれぞれには一意のID(数値)が割り当てられている。 私のグルドンのアカウントは以下のように「1073」という数字が割り当てられている。

f:id:rbtnn:20190414224553p:plain

この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。

f:id:rbtnn:20190414230204p:plain

この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

となる。試すと...

f:id:rbtnn:20190414232404p:plain

ぱっとみわかりにくいが、よくよくみるとカスタム絵文字それぞれの画像URLが取得できていることがわかる、便利。

3) トゥートの情報を取得する

アカウントに一意のID(数値)が割り振られていたようにトゥートにも一意のID(数値)が割り振られている。これも連番である。

GET /api/v1/statuses/:id

もう説明は割愛するが、「:id」が「1」は

f:id:rbtnn:20190414233431p:plain

とそのインスタンスの最初のトゥートが得られる。

ちなみにそのURLの後ろに「/favourited_by」をつけると誰がお気に入りしたかも取得できたりする。

Vim本体のソースコードの読みはじめかた(仮)

github.com

私は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

あと、以下のフォルダも初めは無視しちゃって構わないです。この辺のフォルダは私もまだちゃんと存在意義がわかっているわけではないですが、経験則的に触ったり見たりすることはめったにないです。

  • src/xpm
  • src/xdiff
  • src/GvimExt
  • src/VisVim
  • src/auto
  • src/if_perl_msvc
  • src/libvterm
  • src/os_mac_rsrc

すると残るフォルダは以下の通りとなります。この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ファイルを作成する

「test_hoge.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_BUFENTERgrepしたりするとよいでしょう。

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_typeVAR_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にはオプションがアルファベット順ですべてリストアップされています。 そのオプション変数に対応する変数がポインタで保存されているといった感じです。以下の例では&alephp_alephのポインタを設定していますので、p_alephgrepすると&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の表示言語を日本語にするのは知っていたんですが、表題の件を解決するにはこれだけではたりないっぽいです。

answers.microsoft.com

解決方法はここをみてわかったんですが、どうもWindowsの表示言語の右にある管理用の言語の設定をクリックしてUnicode対応ではないプログラムの言語を日本語に設定しなければならないみたいです。 そしてWindowsを再起動したら無事解決しました。

f:id:rbtnn:20181227230912j:plain

f:id:rbtnn:20181227230335j:plain

タブページを理解しよう

この記事はVim その2 Advent Calendar 2018 - Qiitaの25日目の記事です。

さて、みなさんはVimのタブページをちゃんと理解してますでしょうか? 本記事ではVimのタブページについての説明をしたいと思います。

まず理解しておいてもらいたいのが、Vimタブページはその他のテキストエディタタブと本質的に違います。 Vimタブページは主にウィンドウのレイアウトを保持するものに対して、その他のテキストエディタタブは1つのファイルを保持するものです。

以下の図はこの2つの関係を示したものになります。

f:id:rbtnn:20181226023411j:plain

見てわかる通り、Vimではある1つのファイルが複数のタブページに存在する可能性があります。 逆にその他のテキストエディタタブはある1つのファイルが複数のタブに存在する可能性はありません。

この違いはVimタブページを使いこなすのに重要です。

例えば、Vimタブページをその他のテキストエディタタブのように扱いたいと思ったりすることはあると思いますが、これはかなりの苦労しますし、個人的におすすめしません。

Vimタブページは、1以上のウィンドウを管理し、ウィンドウは1つのバッファを管理します。そして、バッファに1つのファイルが関連づいている感じです。

タブと1つのファイルが密接に関連づいているその他のテキストエディタとはそもそもの仕組みが違いすぎて、同じように扱ったとしても多分あなたはこのタブページとウィンドウとバッファとファイルの管理に一瞬で頭がオーバーフローするでしょう。

あまりまとまりはないですが本記事で言いたいことはこれだけです。要はVimタブページをその他のテキストエディタタブと同じように考えて扱わないで!ということです。この違いを理解すればVimタブページを使う用途がみえてくるんではないでしょうか。 以上、私がちょくちょく開発しているVimの機能TabSidebarのためにタブページユーザーを増やす記事でしたw。

TabSideBarの進捗旅 at 土善旅館

11月23日から26日にかけて三泊四日のVimmer7人で開発合宿を行った。

f:id:rbtnn:20171126234738j:plain

土善旅館での食事とか様子とかは他のVimmerのブログを見てもらうとして、私はやったことをつらつら書きます。

1日目

今回の目標は、WindowsにてTabSideBarの描写がおかしくなるのを修正するというのが一応目標だった。いつもはMacBookAirでもくもく会などに参加しているが、今回はSurfacePro4のWindowsでの参戦。

Windowsの描写まわりが壊滅状態 · Issue #3 · rbtnn/vim · GitHub

描写がおかしくなる現象は以下の通り。スクロールしようとすると、TabSideBar分のスクロール位置がずれる。

f:id:rbtnn:20171127000601g:plain

この現象は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。

f:id:rbtnn:20171127003151g:plain

3日目

3日目はコマンドラインでCtrl-dしてtabキーを押すとwildmenuの描写位置がおかしい問題。

コマンドラインでCtrl-dしてtabキーで表示がおかしい · Issue #6 · rbtnn/vim · GitHub

f:id:rbtnn:20171127004056p:plain

まぁこれは、コマンドラインでCtrl-dしてtabキーを押した時もTabSideBarの幅が考慮されたまま描写されていて、比較的簡単だった。wild_menu_showingという変数がwildmenuの表示状態を保持していて、wildmenuが表示中はTabSideBarの幅を0にしてあっけなく完了。

f:id:rbtnn:20171127004615p:plain

次にマウス対応。この時のTabSideBarは全くマウス対応してない状態。

マウス対応(%T) · Issue #4 · rbtnn/vim · GitHub

で、マウスのイベントはVimのどこで処理しているかと探しているとnormal.cのdo_mouse関数でかなりわかりやすいコードでtablineのマウスクリックイベントが処理されている。screen.cとはUI周りの座標計算ばっかなので全く読みやすくない! tablineのマウスクリックイベント処理をそのままパクって多分TabSideBarのマウスイベントも実装完了。3日目終了。 ただ、TabSideBarにおいて%T%Xはどうするか?という問題は残ったまま保留。

f:id:rbtnn:20171127005513g:plain

4日目

TabSideBarのテストを書くかーと思ったが、Vim本体のテスト実行するmakeのパラメータを忘れている。 なので、めんどくさいことはとりあえずVim scriptで書き書き....

GitHub - rbtnn/vim-runtest

runtest.vimを実行するだけのコマンドが定義してあるVimプラグインを作成して、

:RunTest test_alot.vim

ステーキを食べて4日目終了。

f:id:rbtnn:20171127010511j:plain

個人的には進捗が出た4日間でした。よかったよかった。