2017/02/08


僕は日々 memolist.vim という Vim plugin を使い、仕事で思いついた疑問点や会話の一部をメモ取りする様にしています。相手と会話している最中に「あ、ここ大事だ」と思ったら vim を起動して :MemoNew してメモを編集していました。もちろん Vim ですから起動は抜群に速くてとてもご機嫌良く動くのですが、どうしてもこれをシェルから扱いたいという要求に負けてささっと作ってみました。

GitHub - mattn/memo: Memo Life For You

README.md memo Memo Life For You Usage NAME: memo - Memo Life For You USAGE: memo [global options] c...

https://github.com/mattn/memo
memo

とても単純なコマンドなのです。やりたい事は単純です。

  • 即座にメモ取りを開始したい
  • Markdown で書きたい
  • 簡単に grep したい
  • シェルと融合したい
  • できれば jekyll っぽく markdown をサーブしたい
memo new を実行するとタイトルを聞かれるます。このタイトルがファイル名に使われます。形式は Jekyll の様な日付付きのファイル名です。memo list を実行すると保持しているエントリ一覧を表示します。端末で表示する場合には先頭の行(おそらくタイトル)も表示されます。シェル等でパイプから使う場合にはファイル名のみ渡されます。このファイル名は memo edit xxx の様に編集コマンドに引き渡せます。引数を省略して memo edit とすると流行りのコマンドラインセレクタが起動して編集するファイル名を簡単に絞り込めます。
memo edit

なお、memo list --fullpath にはフルパスにも出来ますのでシェルスクリプトから何かしたい場合にも使えます。さらに memo grep keyword で grep する事もできます。あのメモどこだっけ、という場合に使います。おまけ機能ですが memo serve でウェブサーバが起動する様になっています。ブラウザが勝手に起動して

memo serve

この様なエントリ一覧が表示されます。リンクをクリックすると Markdown が HTML として表示されます。

memo serve

とっさに誰かにメモの内容を見てもらいたい場合には有効かもしれません。設定のデフォルト値を変更したい場合は memo config を実行すると TOML ファイルを指定してエディタ(デフォルトはもちろん Vim)が起動するのでお好きなコマンドセレクタや grep コマンド、テキストエディタに変更して下さい。もちろんワンバイナリで UNIX、Windows どちらでも動く様にしてあります。


2017/02/01


以前からずっと疑問に思っていた事があった。

ruby の後置 if/unless で条件が偽になった場合でも代入構文が実行されるのはどうしてだろう

例えば以下のコードを irb や pry で実行してみて欲しい。

a = 1 if false

続けて a をタイプする。すると nil が表示される。

僕のこれまでの理解だと後置if/unlessは、ステートメントに作用するのでそのステートメント自体が無効になる、つまり代入自体されなかった事になるという理解だった。ruby のパーサのソースコードを見ても後置ifはステートメントに作用している様だった。

        | stmt modifier_if expr_value
            {
            /*%%%*/
            $$ = new_if($3, remove_begin($1), 0);
            fixpos($$$3);
            /*%
            $$ = dispatch2(if_mod, $3, $1);
            %*/
            }
だって raise "foo" if false で例外が飛ばないなら、代入もされないでしょと思っていたけど nil が代入される。この ruby のコードを AST ダンプするとこうなる。
# @ NODE_SCOPE (line: 1)
# +- nd_tbl: :a
# +- nd_args:
# |   (null node)
# +- nd_body:
#     @ NODE_PRELUDE (line: 1)
#     +- nd_head:
#     |   (null node)
#     +- nd_body:
#     |   @ NODE_IF (line: 1)
#     |   +- nd_cond:
#     |   |   @ NODE_FALSE (line: 1)
#     |   +- nd_body:
#     |   |   @ NODE_DASGN_CURR (line: 1)
#     |   |   +- nd_vid: :a
#     |   |   +- nd_value:
#     |   |       @ NODE_LIT (line: 1)
#     |   |       +- nd_lit: 1
#     |   +- nd_else:
#     |       (null node)
#     +- nd_compile_option:
#         +- coverage_enabled: true

これを見ると、AST に落とし込んだ時点でノードテーブルに a が現れている。つまり、後置ifが偽であろうとも代入構文を認識しているという事になる。この AST をどう walk しても代入構文には到達しないよなーと悩んでソースを見たりしたけど良く分からなかったので Matz に直接聞いた。

つまり ruby はこの stmt modifier_if expr_value を見つけると、まずステートメントが何かを判定して代入構文であれば変数 a を用意し、そのあと後置ifを判定して最後に代入という動きを取る。もちろん後置ifが偽であれば代入はされない。よって a は初期化されたままの nil が格納されるという事になる。これを理解した上で以下を見ると、なぜ NameError: undefined local variable or method `a' for main:Object ではなく NoMethodError: undefined method `+' for nil:NilClass なのか理解できた。なるほど深い。

irb(main):001:0> a = a + 1
NoMethodError: undefined method `+' for nil:NilClass
        from (irb):1
        from c:/msys64/mingw64/bin/irb.cmd:19:in `<main>'
irb(main):002:0>

2017/01/25


golang にはパッケージマネージャが無数にあります。

PackageManagementTools · golang/go Wiki · GitHub

Home Articles Blogs Books BoundingResourceUse cgo ChromeOS CodeReview CodeReviewComments CodeTools C...

https://github.com/golang/go/wiki/PackageManagementTools

僕もその一つの gom というのを開発している訳ですが、どれもこれも一長一短でなかなか全ての要望を応えられる物がないのが現状だったりします。ただし、開発者がやりたい事は

今 GOPATH にある、うまくビルドできるバージョンに依存したい

ただこれだけなのです。なんで golang はオフィシャルがこの手のツールを出してくれないんだろう、そう思っていた人も多いと思います。が、それは昨日までの話。

GitHub - golang/dep: Go dependency tool

See the help text for much more detailed usage instructions. Note that the manifest and lock file fo...

https://github.com/golang/dep

でたー!みんな待ってた。そしていつもながら他のツールと名前がバッティングしそうなこの名前の短さ!

使い方も簡単です。まずプロジェクトを作ったら

$ dep init

を実行します。すると lock.jsonmanifest.json が生成されます。この時点では中身はほぼ空っぽです。試しに以下のコードを書いてみます。

package main

import (
    "github.com/mattn/go-runewidth"
)

func main() {
    println(runewidth.StringWidth("あ"))
}

そして以下を実行。

$ dep ensure

すると lock.json が更新されます。

{
    "memo": "e3dcff295c3782f2465033314eee2342535b0a792a61ebf41e0deeab5747a0e7",
    "projects": [
        {
            "name": "github.com/mattn/go-runewidth",
            "version": "v0.0.1",
            "revision": "d6bea18f789704b5f83375793155289da36a3c7f",
            "packages": [
                "."
            ]
        }
    ]
}

dep status を実行すると、go-runewidth の特定のバージョンで stick されているのが分かるかと思います。では試しに

PROJECT                        CONSTRAINT  VERSION  REVISION  LATEST  PKGS USED
github.com/mattn/go-runewidth  *           v0.0.1   d6bea18   v0.0.1  1  

試しに vendor ディレクトリを削除して、go-runewidth のリポジトリで別ブランチに切り替えておきます。そして再度 dep ensure を実行すると、ちゃんと stick したバージョンがよみがえる!

これや!ワイはこれが欲しかったんや!

今のところ、init と status と ensure と remove しかサブコマンドがありませんが、おそらく今後色々と増えてくるんじゃないかなーと思っています。チョー期待してます。

ちなみにいずれ go のサブコマンド(go dep)の様に取り込まれる可能性があるので慎重派の人はしばらくは遊ぶ程度にしておいた方が良さそう。