Humanity

Edit the world by your favorite way

:terminal コマンドが使える Windows 版 Vim の配布状況 (2017/09/15 時点)

tyru.hatenablog.com

この記事を書いた後 Windows でも :terminal コマンドが使える Vim が配布されるようになったのでその配布状況をメモしておく。

Vim バイナリの配布 URL 一覧

まず初めに Vim のバイナリへのリンク集を vim-jp では提供しているので貼っておく。 *1

Redirects

このリンクの「Vim official binary」と付いてるものが :terminal コマンドに対応しています(現時点では)。

方法その(1): Vim official binary を使う

というわけで、Vim official binary のバイナリをダウンロードすれば、すぐに :terminal コマンドが使える GVim が手に入る。

github.com

ただ +kaoriya ではないので細かい部分で差異があります。 例えば本家の (-kaoriya な) Vim だとインサートモードに移ると自動的に IME がオンになってしまいます。 (以下はそのデフォルト値を変えようと本家に働きかけている issue)

github.com

追記(2017/09/17 17:28):8.0.1114 で修正されました。Vim official binary も 8.0.1118 以降のものが取得可能になっています。

これだと <Esc> するにもいちいち IME をオフにしないといけなかったり中々つらいので、 +kaoriya ではデフォルトで IME がオフになるようになっています。 具体的には gvim.exe と同じ場所に置かれた gvimrc というファイルに set iminsert=0 imsearch=0 と設定が書いてあります。

github.com

今 +kaoriya 版 Vim を使っている人で Vim official binary の Vim を使うことで何か違う動作をするようになったらここらへんの設定を持ってくれば Vim script レベルでは +kaoriya Vim と同じ設定になるはずです。 (とはいえ初心者には原因究明は辛そうなので vim-jp/issues で質問してしまってもいいです)

Vim script レベル」と言ったのは、+kaoriya は vimrc のデフォルト設定の他、日本語圏で使うために便利な様々なパッチを Vim 本体の C のソースにも当てています。 どんなパッチを当てているかは私は知らないので興味があれば調べてください。 多分ここら辺で管理されてるんじゃないかなぁと思います。たぶん。

方法その(2): 自前でビルドする

Windows 向けにビルドする方法はこちら。 僕はやったことないので知りません。にわか Vimmer なので…

vim-jp.org

こちらの方の記事もどうぞ。

abrakatabura.hatenablog.com

Vim official binary についてもうちょっと詳しく

github.com

自分のメモのためにももうちょっと詳しく書いておく。

そもそも Vim official binary (あるいは Vim nightly build) とは k-takata さんが作成してその後 Vim 本家にマージされた、 WindowsVim を CI でビルドするためのリポジトリ

毎日ドイツ時間 (GMT+2) の0時にビルドされてるとのこと。 *2

Vim の :terminal コマンドの実装は Windows の場合 winpty が使われているので、動作には winpty.dll が必要。*3 この DLL のビルドに対応したのが Vim 8.0.0961 からで、vim-win32-installer リポジトリでは先行してパッチを当てていたので、8.0.0960 のバイナリでも :terminal コマンドが使えるようになっているらしい。 *4

つまり自分で VimリポジトリWindows 向けにビルドしようとすると 8.0.0961 以降、 vim-win32-installer リポジトリのバイナリをダウンロードして使う場合には 8.0.0960 以降であれば :terminal コマンドが使える。

ただ :terminal コマンドは追加されてからもどんどん機能追加やバグ修正が行われているので、:terminal コマンドを使おうと思ったらなるべく新しいバージョンを使うのがベターです。 新しいバージョンであれば :terminal コマンドが安定して動作する…訳では(まだ)ないですが、新しいバージョンであればあるほどバグ報告して直る可能性が高く、つまり Vim 開発者のサポートが得られます。

冒頭の記事でも書きましたが、報告する場所は vim-jp/issues に書けば問題ないです。 新しく issue 建てるのが恥ずかしいシャイな人はこちらにコメントしても構いません(以下は :terminal コマンドの総合 issue スレみたいになってるのでバグが直ってるか確認する場合にも見るといいかもしれません)。

github.com

:tmap コマンド

ちなみに関係ないですが、最近は 8.0.1108 で :tmap というコマンドが追加されたので、 :terminal で開いたバッファ特有のマッピングをすることができま…アアー!! このコマンド今 Vim official binary のリポジトリで配布されてる最新版では使えない! 毎日ビルドされてるはずなのでビルドエラーが起きてるんですかね… まぁそのうち使えるようになるんじゃないかと思います。

とりあえず使い方だけ書いておくと、他の :map 系コマンドと同じくこんな感じです。 Ubuntu on Windows では Vim 8.0.1111 で動作するのを確認しています。

" ターミナルウインドウで入力中でもノーマルモードでも同様にタブ移動するためのマッピング (サンプル)。
" <C-w><C-n> はデフォルトで新規ウインドウ作成に割り当てられているが、
" これをタブ移動に割り当てる (<C-w><C-n> で右のタブ、<C-w><C-p> で左のタブ)
if exists(':tmap') ==# 2
  " Terminal-Job モード用
  tnoremap <C-w><C-n> <C-w>:tabnext<CR>
  tnoremap <C-w><C-p> <C-w>:tabprevious<CR>
  " Terminal-Normal, Normal モード用
  nnoremap <C-w><C-n> :<C-u>tabnext<CR>
  nnoremap <C-w><C-p> :<C-u>tabprevious<CR>
endif

ついでにコメントで書いた Terminal-Job モードと Terminal-Normal モードについてざっくり説明すると、 Terminal-Job モードは terminal window で入力可能なモードのことです。 Terminal-Normal モードは terminal window で <C-w>N で遷移できるモードのことで、コマンドの出力をヤンクして他のバッファに貼り付けたりできます。便利。

追記(2017/09/15):Vim official binary の最新版がビルドエラーの件

とのことです。残念。

さらに追記(2017/09/17 17:28):ビルドエラーが修正された

下記のリリース (v8.0.1118) で修正されました!

github.com

ちなみにこのリリースは iminsert と imsearch のデフォルト値も 0 (=インサートモード開始時に IME がオフ) になるようになってます。 色んな問題が片付いていってスピード感がありますね…

Go の net/http で Vue.js / Angular 1 などの HTML5 history mode に対応する

type html5Handler struct {
    fs     http.FileSystem
    routes []staticRoute
}

// Look up path when Vue.js HTML5 history mode is enabled
// https://router.vuejs.org/ja/essentials/history-mode.html
func enableHTML5Mode(fs http.FileSystem, routes []staticRoute) http.FileSystem {
    return &html5Handler{fs, routes}
}

func (handler *html5Handler) Open(p string) (http.File, error) {
    file, err := handler.fs.Open(p)
    if err != nil {
        return searchHTML5Mode(handler, p, err)
    }
    return file, err
}

// * Try again after removing prefix if it has
// * Returns index.html otherwise
func searchHTML5Mode(handler *html5Handler, p string, err error) (http.File, error) {
    p = path.Clean(p)
    for _, route := range handler.routes {
        if strings.HasPrefix(p, route.basePath) {
            file, err := handler.fs.Open(strings.TrimPrefix(p, route.basePath))
            if err == nil {
                return file, nil
            }
        }
        if route.indexPattern.MatchString(p) {
            return handler.fs.Open("/index.html")
        }
    }
    return nil, err
}

http.Handle("/", http.FileServer(http.Dir("assets/site")))

とかやってたところを

routes := []staticRoute{
    staticRoute{indexPattern: regexp.MustCompile(`^/feeds/[0-9]+$`), basePath: `/feeds`},
}
http.Handle("/", http.FileServer(enableHTML5Mode(http.Dir("assets/site"), routes)))

に変える。

そうすると routes に定義したパス(この場合は /feeds/:id)に GET でアクセスした場合にも index.html が返り、 index.html の <script> タグや <img> タグ等で指定した静的ファイルも(basePath が取り除かれて再検索されるので)返るようになる。 もちろんもともと http.Dir() で指定してたパスの階層とマッチする場合は今まで通り問題なく静的ファイルが返される。

今回やったのは func (FileSystem) Open(p string) (http.File, error) で第2返り値の error が non-nil だった場合に指定された routes で再検索してるだけです。

ちなみに path.Clean(p)ディレクトリトラバーサル対策です。便利。

合わせて読みたい

www.kaoriya.net