Sublime Text 3のライセンス料について

@syohex さんと @kaoriya さんに向けて

LL Diverの日にsyohexさんにSublime Textのライセンスについて聞かれたんですが、かなり適当なことを言ってしまったので訂正します。申し訳ありません。
Sublime Text 3は現在ベータ版で、正式版がリリースされたら新しくライセンスを買う必要があると言ってしまいましたが、これは間違っていました。正しくは、

  • 2013/1/29 2:47PM以降に購入したライセンスは正式版がリリースされても追加料金なしで使うことができます。
  • 2013/1/29 2:47PM以前に購入したライセンスは正式版のリリース時にアップグレード料金を払う必要があります。

つまり、今買えばSublime Text 3の正式版にアップグレード料金は必要ないです。
なお、どちらの場合でもSublime Text 4を使うにはアップグレード料金が必要だそうです。
参考リンク
Sublime Blog » Upgrades
Sublime Text 2 - Sales FAQ
Sublime Forum • View topic - Have the ST 2 licence terms changed?

Sublime Textのライセンス料はやっぱり高い

あの場で「カスタマイズして遊べるおもちゃだと思えば70ドルは高くない」というようなことを言いましたが、後で考えなおすと意味不明でしたね… よく考えたんですが、やっぱり70ドルは高いですよね。これは別に数日で考えが変わったわけではなく、私自身はSublime Textに価値を感じてライセンスを買いましたが、普通の人にしてみれば70ドルはちょっと高いと感じる値段だろうなと思い直しました。

LL Diverのエディタ対決に参加してきた

Sublime Text担当として参加してきました。関係者の方はお疲れ様でした。また会場で見てくださった方、ありがとうございました。

全般的にSublime Textではこれができない、ということばかり言ってしまったと思います。もっと他に言うことはなかったのかと自分でも思います。 あと最後の方はテンパりすぎて自分でも何を言っているのかよくわからなくなってしまいました。

未来のエディタについて

未来のエディタに欲しい機能として自動補完を挙げましたが、正直みんな「は?」って思ったんじゃないでしょうか。すでに現在のエディタが備えている機能ですし。
元々、Light TableのInstaREPLを参考にして、コードを書いたらすぐに自動実行して評価結果をコードの横に表示してくれる機能がいろんな言語・エディタに対応して欲しい云々という話をしようと思っていたんですが、あの場では完全に失念してしまい、苦し紛れに適当なことを言ってしまいました。
適当なことを言った上、その後フォローもできないのは自分でもどうかと思っています。

Sublime Textのターミナル版

あの場ではSublime Textのターミナル版は自分はあっても別に嬉しくないと言いました。それはその通りなんですが、LimeText(http://limetext.org/)というSublime Textのクローンをオープンソースで実装するプロジェクトはターミナル版のフロントエンドを開発しているそうです。あの場で言えれば良かったんですが、これも忘れていました。

以下、会場で言えなかったことを含めて各エディタの自分の考えを書きます。

Vim

Vimが一番優勢だったような気がします。kaoriyaさんが説得力のある話をしていましたし、Vimを使ってる人が一番多そうでした。 私も単にVimを使うだけなら好きです。でも諸々の理由でVim scriptがあまり好きではないのでなるべくカスタマイズせずに使いたい…

Atom

Atomは今一番ホットなエディタだと思うのでどんどん進化していって欲しいと思います。 現状のAtomに関して言えば、1フォルダ1ウィンドウになっているのが使いづらいのでこの辺りが課題だとおもいます。こうなっている理由があるのなら是非知りたい。

Emacs

syohexさんがEmacsMacで使いづらいという話をしていましたが、正直これはよくわかりませんでした。ちょうどこの前Macbookを買ったので自分でも使ってみて確認しようと思います。
あと疑問に思っているんですが、Org-modeのソースを見ると、letで変数宣言しておいて、letのスコープの中でsetqで変数を書き換えまくるという手続き的なコーディングをよく見かけるんですが、Emacs Lispではそれが普通なんでしょうか…

Sublime Text

開発元の更新が滞りがちなので非常に未来が不安です。と言ってもちょうど今日、約3ヶ月ぶりに開発版がアップデートされました。

以上です。

無限ループを簡易的にチェックする

Pythonを書いているときに無限ループを簡易的にチェックする方法を思い付いたのでメモ。 以下のように無限ループに陥ってしまったとします。

cond = True
while cond:
    cond = True  # いつまでたっても条件がFalseにならない
    print('hoge')

このとき、とりあえず本来のループ脱出条件は無視して無限ループから抜け出そうと思い、イテレータを使う方法を思い付きました。

cond = True
limit = iter(range(10000))
while cond:
    next(limit)  # 無限ループチェック
    cond = True  # いつまでたっても条件がFalseにならない
    print('hoge')

このようにすれば、10001回目のループの先頭でStopIteration例外が発生して、無限ループから抜けることができます。

※range関数を使っていますが、Python2ではxrangeを使ったほうがよいでしょう。

Python3でunbound methodが無くなった

Pythonでクラスやインスタンスの属性に関数を代入して呼び出すコードを書いていたらPython2とPython3で違いがあることが わかりました。

最初に状況を説明すると、第一引数にselfを受け取らない単なるグローバル関数をクラス属性として保持しておいて、 後から呼び出すコードを書いていたら、Python3では動作したのにPython2で例外が発生しました。

この挙動の違いはPython3で unbound method という概念が無くなったのが原因でした。

まず動作確認に使用したソースを示します。

def func(*args):
    print(args)


class MethodTest(object):
    pass


c = MethodTest()

# インスタンス属性としてグローバル関数を代入して呼び出すと単なる関数として呼び出される
c.method = func
c.method('hello')  # => ('hello',)
print(c.method)  # => <function func at 0x00000083FEB001E0>
del c.method

# クラス属性としてグローバル関数を代入してインスタンスから呼び出すとbound methodとして呼び出される
MethodTest.method = func
c.method('hello')  # => (<test.MethodTest object at 0x00000069D8AAE3C8>, 'hello')
print(c.method)  # => <bound method MethodTest.func of <test.MethodTest object at 0x00000083FF474860>>

# クラス属性としてグローバル関数を代入してクラスオブジェクトから呼び出すと…
# Python 3.3: 単なる関数として呼び出される
MethodTest.method = func
MethodTest.method('hello')  # => ('hello',)
print(MethodTest.method)  # => <function func at 0x00000083FEB001E0>

# Python 2.6, 2.7: unbound methodをself無しで呼び出そうとしてTypeError例外が発生する
MethodTest.method('hello')  # => TypeError: unbound method func() must be called with MethodTest instance as first argument (got str instance instead)
print(MethodTest.method)  # => <unbound method MethodTest.func>

以下の2点はPython2/3で違いはありません。

  • インスタンス属性としてグローバル関数を代入して呼び出す
  • クラス属性としてグローバル関数を代入してインスタンスから呼び出す

ただし、後者はbound methodとして呼び出されているので、func関数は第一引数に意図しないself(MethodTestオブジェクト)を受けとってしまっています。

一方、クラス属性としてグローバル関数を代入してクラスオブジェクトから呼び出す場合は以下の通りPython2と3で動作が違います。

  • Python 3.3: 単なる関数として呼び出される(私が期待した通りの挙動)
  • Python 2.6, 2.7: unbound methodをself無しで呼び出そうとしてTypeError例外が発生する

Python2系では、メソッドの呼び出し方が間違っているのでエラーが出ました。 これは、MethodTest.method の第一引数に MethodTest のインスタンスを渡すことで呼び出すこと自体はできます。

c = MethodTest()
MethodTest.method = func
MethodTest.method(c, 'hello')  # => (<MethodTest object at 0x0000000004919BE0>, 'hello')

ただ、今回は単なる関数として呼び出すのが目的なのでこれでは解決になっていません。これを解決するにはstaticmethodを使って以下のようにします。

MethodTest.method = staticmethod(func)
MethodTest.method('hello')  # => ('hello',)
print(MethodTest.method)  # => <function func at 0x00000083FEB001E0>

staticmethodにより、unbound methodがfunctionに変わっているので、単なる関数として呼び出せます。

まとめ

  1. Python2ではunbound methodの概念があり、Class.method でメソッドを取得すると unbound method になる。
  2. unbound methodの呼び出しでは渡された第一引数の型をチェックし、Classまたはそのサブクラス以外が渡されたらTypeErrorを発生する。(←この説明はちょっと自信ない)
  3. それを避けて単なる関数として呼び出すにはstaticmethodを使う
  4. Python3ではunbound methodの概念が削除されたため、Class.method で取得すると単なる関数になる。そのため、Python2でやっていたような第一引数の型チェックは行われない。

Pythonに関してあまり詳しくないので、間違いがあったらご指摘等お願いします。

submode.vim/smartrep.el と同じことをする

submode.vim や smartrep.el は、VimEmacsで連続したキー入力を楽にできるプラグインです。詳しい説明は id:thinca さんの記事が参考になります。 submode.vim とその設定例なんかを紹介

このエントリではSublime Textで同じことを、1行もコードを書かずに、キーバインドの定義だけでやってみます。

たとえば、次のように ctrl+k, n で次のブックマークへ移動、ctrl+k, p で前のブックマークにカーソルを移動するキーバインドを定義します。

// Default.sublime-keymap
[
    { "keys": ["ctrl+k", "n"], "command": "next_bookmark" },
    { "keys": ["ctrl+k", "p"], "command": "prev_bookmark" }
]

これで連続して次のブックマークに移動しようとすると、 ctrl+k, n, ctrl+k, n, ctrl+k, n, ... というように、キーを交互に押さなければならないため面倒です。 これを、 ctrl+k, n, n, n, ... というように最初以外の ctrl+k を省いて入力できるようにする方法を説明します。

設定

キーバインドを追加して、次のようにします。

// Default.sublime-keymap
[
    { "keys": ["ctrl+k", "n"], "command": "next_bookmark" },
    { "keys": ["ctrl+k", "p"], "command": "prev_bookmark" },
    { "keys": ["n"], "command": "next_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "next_bookmark"}
        ]
    },
    { "keys": ["p"], "command": "prev_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "prev_bookmark"}
        ]
    }
]

この設定で、 ctrl+k, n, n, n, ...ctrl+k, p, p, p, ... でブックマーク間の移動ができるようになります。

解説

np に対してブックマークの移動コマンドを割り当ててしまっていますが、通常時にこれらのキーを押してもブックマークの移動にはなりません。 この設定の肝はキーバインド定義中の "context" の部分です。

{ "keys": ["n"], "command": "next_bookmark",
    "context": [
        {"key": "last_command", "operator": "equal", "operand": "next_bookmark"}
    ]
}

この "context" の内容は "last_command" が "operand" に指定されている "next_bookmark" と一致するかをチェックする、というものです。 つまり "next_bookmark" というコマンドが直前に実行されている時に限り、このキーバインドが有効になります。 その他の場合、通常通り n が入力されます。

サブモードっぽくする

上記の設定では ctrl+k, n を押したあとに p を押しても通常通り p が入力されるだけで前のブックマークに移動できません。 次のようにすると、 ctrl+k を押したあと、 np のどちらでも連続して移動できるようになります。

// Default.sublime-keymap
[
    { "keys": ["ctrl+k", "n"], "command": "next_bookmark" },
    { "keys": ["ctrl+k", "p"], "command": "prev_bookmark" },
    { "keys": ["n"], "command": "next_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "next_bookmark"}
        ]
    },
    { "keys": ["n"], "command": "next_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "prev_bookmark"}
        ]
    },
    { "keys": ["p"], "command": "prev_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "next_bookmark"}
        ]
    },
    { "keys": ["p"], "command": "prev_bookmark",
        "context": [
            {"key": "last_command", "operator": "equal", "operand": "prev_bookmark"}
        ]
    }
]

キーバインドの内容は、単純に n を押したあとの p と、 p を押したあとの n を追加しているだけです。 next_bookmark と prev_bookmark が別のコマンドなのでこのようにする必要があります。

この冗長なキーバインドを何とかしたい場合は、ここでは説明しませんが、Pythonコードを書いてプラグインを作るしかないでしょう。

他に以下のような "operator" に "regex_match" を使う方法を思いつきましたが、動作しませんでした。context の key によっては "regex_match" は使えないようです。

// 動作しない
[
    { "keys": ["n"], "command": "next_bookmark",
        "context": [
            {"key": "last_command", "operator": "regex_match", "operand": "(next|prev)_bookmark"}
        ]
    },
    { "keys": ["p"], "command": "prev_bookmark",
        "context": [
            {"key": "last_command", "operator": "regex_match", "operand": "(next|prev)_bookmark"}
        ]
    }
]

連続入力状態から抜ける

np を押すのを止めて次の操作に移るときは何もせずに次の操作をすればいいです。しかし、 n や p を文字として挿入する場合はその前にキャンセル動作が必要となります。 キャンセルするには 何か別の動作を行って "last_command" を next_bookmark と prev_bookmark 以外にする必要があります。例えば、カーソルを動かせばキャンセルできます。

この方法が使えるコマンドについて

これまで解説した方法は、全てのコマンドで使えるわけではありません。しかも、どのコマンドが使えるのかの正確な情報は、探しても見つけられませんでした。 たとえば、左右のタブに切り替えるには next_view と prev_view が使えますが、上で解説した方法をこれらのコマンドで使おうとしても動作しませんでした。 私の予想ですが、おそらく使えるのは現在のタブに対して影響を与えるようなコマンドだけだと思います。カーソル位置の移動や、文字の挿入、削除などの動作がそれに当たります。 ユーザー定義のコマンドであれば、 sublime_plugin.TextCommad を継承しているコマンドです。

ちなみに、Sublime Text のマクロ機能にも同じような制限があり、マクロの記録中にタブを移動してもそれは記録されません。

参考リンク: http://sublimetext.userecho.com/topic/79139-recorded-macros-should-not-ignore-movement-commands/

まとめ

submode.vim/smartrep.el と似たような機能を、キーバインドの設定だけで実現できます。 Sublime Text のキーバインド設定は極めて柔軟であると言えます。 上に書いたような制限があるものの、活用してみてはいかがでしょうか。