2020/02/10


vim-jp の Slack で「zsh の PATH 環境変数に相対パスを含んでいる場合、補完ができないけど意図的か」という話題が出たので調べてみた。

補完できない様にしているのはこの変更

39104: do not hash relative paths in findcmd() · zsh-users/zsh@b312abc
https://github.com/zsh-users/zsh/commit/b312abc93b3b8eae8feb4a9884b22f519a137c7f

結構古い変更。この変更が行われた理由を追ってみた所、メーリングリストでこの会話が見つかった。

Running 'type' causes false positive hashed command completion

Zsh Mailing List Archive Messages sorted by: Reverse Date , Date , Thread , Author Running 'type' ca...

http://www.zsh.org/mla/workers/2016/msg01583.html
$ zsh -f
% cd $(mtemp -d)
% touch sudofoo; chmod +x $_
% ./sudo<TAB>
<becomes>
% ./sudofoo <^C>
% type -w ./sudo
./sudo: none
% ./sudo<TAB>
./sudo    sudofoo*

That's wrong because ./sudo does not exist.  However, it's hashed:

% print $commands[./sudo]
/usr/bin/./sudo

To confuse matters further, even though "./sudo" is hashed, a subsequent
'type -w ./sudo' will print "none", because the hash node lacks the
HASHED bit in its .flags and the PATH_DIRS option is unset by default.

/usr/bin 対しては ./sudo が存在する為、sudofoo に対する ./sudo からの補完候補に sudo が出てきてしまう、これは混乱を生んでしまう」という物だった。これを回避する為に上記の変更で相対パスはハッシュしない様にしている。ちなみに bash や fish だと相対パスの中のコマンドも補完された。

相対パス上で sudofoo の一部 sudo が補完されてしまったとして困るのは、その相対パス内に危ないファイルを追加してしまった場合だろうと推測するが、そもそも相対パスを PATH に追加したい要件が僕には見つからなかった。おそらく自動的に node_modules/.bin 内のコマンドを扱える様にしたいといった物だと思う。もし zsh でやりたい人は direnv を使って動的に PATH を追加するのが良いと思う。

Posted at by



2020/01/20


昨年から Oracle Cloud の無料枠を使っています。2 vCPU な VM を2台も無料で使わせて頂けるという Oracle Cloud さんの大盤振る舞いに感謝しつつ、Oracle Cloud Function でしりとりをしてみました。

Oracle Cloud Function は Fn Project というサーバレスプラットフォームをベースにしており、同プロジェクトの fn というツールを使う事で、他の Fn Project を使うクラウドと同様に操作を行う事ができます。
Fn Project

Open Source. Container-native. Serverless platform.

https://fnproject.io/

fn コマンドは以下の手順でインストールする事ができます。

curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh

MacOS の場合は homebrew からインストールが可能。

brew update && brew install fn

Windows の場合は僕が送っている pull-request を使わないとエラーになります。ソースを git clone して go build して下さい。

Fix atomicwrite for Windows by mattn - Pull Request #605 - fnproject/cli
https://github.com/fnproject/cli/pull/605

fn コマンドを使える様になるまでは sugimount さんが Qiita に書いている記事を参考にしました。

サーバレスな Oracle Functions (Fn) をやってみた - Qiita
https://qiita.com/sugimount/items/018e08f575ecefb1546c

注意点としては、Oracle Cloud はデフォルトで VCN が作られていますが、Function を使う場合は別途 VCN を作らないと DHCP オプションが一致しないというエラーが出てしまいます。新しく作って下さい。あと sugimount さんが書かれているリポジトリ名と異なる物が実際には用意されるので、詳細は Oracle Cloud のダッシュボードからファンクションを選択し、アプリケーション詳細の「開始」タブを見るとほぼやるべき事が書いてあります。

Oracle Cloud

アプリケーションを作る所まで出来たら、しりとりサーバを作ります。短いのでコード全体を載せます。

package main

import (
    "bufio"
    "context"
    "encoding/json"
    "errors"
    "io"
    "math/rand"
    "strings"

    _ "func/statik"

    fdk "github.com/fnproject/fdk-go"
    "github.com/rakyll/statik/fs"
)

var upper = strings.NewReplacer(
    "ぁ""あ",
    "ぃ""い",
    "ぅ""う",
    "ぇ""え",
    "ぉ""お",
    "ゃ""や",
    "ゅ""ゆ",
    "ょ""よ",
)

func kana2hira(s stringstring {
    return strings.Map(func(r runerune {
        if 0x30A1 <= r && r <= 0x30F6 {
            return r - 0x0060
        }
        return r
    }, s)
}

func hira2kana(s stringstring {
    return strings.Map(func(r runerune {
        if 0x3041 <= r && r <= 0x3096 {
            return r + 0x0060
        }
        return r
    }, s)
}

func search(text string) (stringerror) {
    rs := []rune(text)
    r := rs[len(rs)-1]

    statikFS, err := fs.New()
    if err != nil {
        return "", err
    }
    f, err := statikFS.Open("/dict.txt")
    if err != nil {
        return "", err
    }
    defer f.Close()
    buf := bufio.NewReader(f)

    words := []string{}
    for {
        b, _, err := buf.ReadLine()
        if err != nil {
            break
        }
        line := string(b)
        if ([]rune(line))[0] == r {
            words = append(words, line)
        }
    }
    if len(words) == 0 {
        return "", errors.New("empty dictionary")
    }
    return words[rand.Int()%len(words)], nil
}

func shiritori(text string) (stringerror) {
    text = strings.Replace(text, "ー""", -1)
    if rand.Int()%2 == 0 {
        text = hira2kana(text)
    } else {
        text = kana2hira(text)
    }
    return search(text)
}

func handleText(text string) (stringerror) {
    rs := []rune(strings.TrimSpace(text))
    if len(rs) == 0 {
        return "", errors.New("なんやねん")
    }
    if rs[len(rs)-1] == 'ん' || rs[len(rs)-1] == 'ン' {
        return "", errors.New("出直して来い")
    }
    s, err := shiritori(text)
    if err != nil {
        return "", err
    }
    if s == "" {
        return "", errors.New("わかりません")
    }
    rs = []rune(s)
    if rs[len(rs)-1] == 'ん' || rs[len(rs)-1] == 'ン' {
        s += "\nあっ..."
    }
    return s, nil
}

func main() {
    fdk.Handle(fdk.HandlerFunc(myHandler))
}

type Siritori struct {
    Word string `json:"word"`
}

func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
    var s Siritori
    json.NewDecoder(in).Decode(&s)
    var err error
    s.Word, err = handleText(s.Word)
    if err != nil {
        s.Word = err.Error()
    }
    json.NewEncoder(out).Encode(&s)
}

辞書ファイルは statik を使ってバイナリに埋め込みました。ソースコードは GitHub に置いておきます。

mattn/oracle-cloud-function-siritori
https://github.com/mattn/oracle-cloud-function-siritori/

デプロイは以下の手順で行います。

$ fn  --verbose deploy --app [your-app]

デプロイが完了すると標準入力で JSON を受け取り、標準出力で JSON を出力するコマンドが動く様になっています。

Oracle Cloud

VCN を作らないといけない事に気付くまで結構時間を使ってしまったけど、動く事が分かってからは結構サクサク操作できる様になりました。これだけ遊んでもまだ無料範囲内らしいので、もう少し遊んでみたいと思います。

Posted at by



2020/01/06


はじめに

この文章は、普段から Vim を使い、仕事でも趣味でも Go 言語を書いている僕が、最近どの様な環境で書いているかを説明した文章です。ベストプラクティスではありません。

vim-go と僕

元々、Go 言語はリポジトリの misc/vim に Vim で Go 言語を書くための syntax やコマンドを持っていました。今でもそれらは Google のリポジトリに置かれています。ミュージアム的な物なので、実用的ではないと思います。

GitHub - google/vim-ft-go

A rudimentary Go filetype plugin. Provides syntax files and basic settings for go files. This is a f...

https://github.com/google/vim-ft-go

これを Fatih Arslan 氏が拡張した vim-go が、最近では Vim で Go 言語を書くためのデファクトスタンダードになっていました。vim-go は :GoImport コマンドによる import 文の追加や、:w での保存時にソースコードを整形する機能、:GoDef による定義位置ジャンプなど、色々な機能を搭載していきました。

僕もずっと vim-go を愛用してきて、幾らかコントリビュートもさせて頂いたりしました。正直、vim-go が無いと Go 言語が書けない、そんな風にも思っていました。

大きくなりすぎた vim-go

最近、vim-go が Language Server Client を実装しました。僕は vim-lsp という Language Server Client を使っていたのでそれらを無効にしていました。少し残念だったのが vim-go の Language Server Client の実装に伴い、幾らかバグが発生していました。またシンタックスハイライトにもバグがありました。そしてこれは最近思い始めた事ではないのですが、幾らかの機能を無効にしていても「若干重たいな」と感じていました。

vim-go1
vim-go2

普段から vim-go の幾らかの機能を無効にする為の設定を vimrc に書いていたのですが、ふと「もしかして僕には vim-go は大きすぎるのかもしれない」と思ったのです。

僕が欲しい機能は何だ

改めて、僕が Vim による Go 言語の編集環境で欲しい物を洗い出してみました。

  • Go 言語のシンタックスハイライト
  • 入力補完
  • :GoImport コマンド
  • :w による自動ソースコード整形、およびそのエラー表示

これだけあれば、僕には十分なのです。

Go 言語のシンタックスハイライトに関しては、最近の Vim であればデフォルトランタイムに含まれています。Go 言語は新しい予約語を増やす事はないので、これで十分です。

入力補完に関しては vim-lsp を使っており、こちらの方が色々な言語をサポートしており拡張性もあり、そもそも僕は vim-lsp のコントリビュータでもあるのでこちらが使いたいです。ただし、実は僕は入力補完は使っていなかったりします。Vim による編集の体験を良くする為に Vim や vim-lsp へのコントリビュートは行っていますが、僕本人はほぼ手打ちしていたります。

残るは :GoImport コマンドと :w による自動ソースコード整形なのですが、これだけの為に vim-go に依存する必要はないと思い立ち、vim-go を卒業する事を決めました。

vim-goimports による引越し

GitHub - mattn/vim-goimports: Vim plugin for Minimalist Gopher

vim-goimports Vim plugin for Minimalist Gopher Features Auto-formatting with :w GoImport/GoImportAs ...

https://github.com/mattn/vim-goimports

vim-goimports は、上記の残る2つの問題を解決する為だけに作ったプラグインです。

  • :GoImport コマンド
  • :w による自動ソースコード整形、およびそのエラー表示

メンテナンスやバグ修正は行いますが、これ以上、機能を足す予定はありません。もし「あー、vim-go のあの機能はさすがに欲しかった」と思い出す事があれば、僕はおそらく別のプラグインを作るでしょう。

これは vim-go がイケてないという話ではなく、僕と vim-go との距離感の話です。僕が vim-go に歩みよるよりも、僕のスタイルに何かを足して僕が欲しかった物を作った方が近かった、というだけの話です。

Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
Plug 'prabirshrestha/vim-lsp'

Plug 'mattn/vim-lsp-settings'
Plug 'mattn/vim-goimports'

Language Server や入力補完が必要ない方であれば以下でも構いません。

Plug 'mattn/vim-goimports'

これにより、vim-go の幾らかの機能を無効にする為に書いていた設定類を消しました。:GoDef による定義位置ジャンプは vim-lsp が有効になっていれば gd で可能ですし、ソースコードの整形は vim-goimports がやってくれます。ソースコード整形は、本来 Language Server でやれる話ですが、これに関しては絶賛取り組み中で、もう少し時間が必要です。それまでの間は vim-goimports に頼る事にします。また下記のエントリでもご紹介した通り、Vim の歩むべき道は、個別の言語の個別のプラグインによって個々のツールの導入に時間を割いたり vimrc を太らせる事ではないはずなのです。

Big Sky :: Vim をモダンな IDE に変える LSP の設定

Go 言語の IDE 機能を得る為に何か知る必要はありません。Java の IDE 機能を得る為に何か知る必要はありません。HTML の IDE 機能をインストールする為に npm コマンドの使い方を...

https://mattn.kaoriya.net/software/vim/20191231213507.htm

おわりに

上記の引越し作業により、幾分 vimrc の Go に関する項目がスッキリしました。今後、本来は必要なはずだった vim-go の機能が見つかれば、自分で作っていくしかありません。ですが 2019 年末に「こっちの方が近い」と思った自分を信じて、しばらくはやってみます。

Posted at by