keyhacでクリップボード内のJavaScriptコードを整形/圧縮(JSBeautifier/SlimIt)

JavaScriptをさわってる人ならご存知の人も結構いそうなOnline JavaScript beautifierJavaScriptコードを見やすく整形してくれるWebサービスです。
僕はブックマークレットを作る際など便利でよく使っているのですが、サイトを見てるとコマンドラインバージョンということでPythonコードでも公開されていました。探したらSlimItという圧縮する方も発見。
keyhac*1クリップボード内のテキストに対して直接実行できたら便利だろうなと書いたのが ↓ のコード。
Win+Jで整形、Win+Sift+Jで高圧縮、Win+Ctrl+Jで低圧縮、というキー配置にしています。

from keyhac import *

## 関数を返す関数にするデコレータ
def ret_func(func):
    import functools

    @functools.wraps(func)
    def _ret_func(*args, **kw):
        @functools.wraps(func)
        def __ret_func():
            return func(*args, **kw)
        return __ret_func
    return _ret_func

## JobQueue/JobItem でサブスレッド処理にするデコレータ
def job_queue(func):
    import functools

    @functools.wraps(func)
    def _job_queue(*args, **kw):

        num_items = JobQueue.defaultQueue().numItems()
        if num_items:   # 処理待ちアイテムがある場合は、その数を表示
            print u"JobQueue.defaultQueue().numItems() :", num_items

        def __job_queue_1(job_item):
            return func(*args, **kw)

        def __job_queue_2(job_item):
            # print "job_queue : ", func.__name__, args, kw
            pass

        job_item = JobItem(__job_queue_1, __job_queue_2)
        JobQueue.defaultQueue().enqueue(job_item)

    return _job_queue

## JavaScriptコードを整形
# JSBeautifier : http://jsbeautifier.org/
@job_queue
def jsbeautifier():
    import jsbeautifier
    clipboard_text = getClipboardText()
    if clipboard_text:
        js_options = jsbeautifier.default_options()

        js_options.jslint_happy = True
        new_code = jsbeautifier.beautify(clipboard_text, js_options)

        setClipboardText(new_code)
        print
        print "/*  jsbeautifier  */"
        print "// before --------------------"
        print clipboard_text
        print "// after --------------------"
        print new_code

## JavaScriptコードを圧縮
# SlimIt : http://slimit.org/
# PLY : http://www.dabeaz.com/ply/
@ret_func
@job_queue
def jsminify(mangle=False):
    import slimit
    clipboard_text = getClipboardText()
    if clipboard_text:
        new_code = slimit.minify(clipboard_text, mangle)

        setClipboardText(new_code)
        print
        print "/*  SlimIt (mangle=%s)  */" % mangle
        print "// before --------------------", len(clipboard_text), "byte"
        print clipboard_text
        print "// after --------------------", len(new_code), "byte"
        print new_code

def configure(keymap):

    keymap_global = keymap.defineWindowKeymap()

    # JavaScriptコードを整形
    keymap_global["W-j"] = jsbeautifier

    # JavaScriptコードを圧縮(高圧縮)
    keymap_global["W-S-j"] = jsminify(mangle=True)

    # JavaScriptコードを圧縮(低圧縮)
    keymap_global["W-C-j"] = jsminify(mangle=False)

■ ライブラリのインストール

・JSBeautifier

https://github.com/einars/js-beautify/raw/master/python/jsbeautifier.py
↑ のファイルをkeyhacのextensionフォルダへコピーする。

・SlimItとPLY(コード解析)

https://github.com/rspivak/slimit
圧縮ファイルをダウンロードし、圧縮ファイルのsrcフォルダ下にslimitをフォルダごとkeyhacのextensionフォルダへコピーする。
http://www.dabeaz.com/ply/
圧縮ファイルをダウンロードし、圧縮ファイルのplyをフォルダごとkeyhacのextensionフォルダへコピーする。

■ さいごに

ブックマークレットをよく書くという方は特に便利だと思うので、よかったら使ってみてください。

■ 参考リンク

*1:Autohotkeyみたいなフリーソフトで、Pythonコードで色んなソフトのショートカットキーを設定できる

keyhacのPythonスクリプトにも便利なデコレータ

引き続き、keyhacネタ。keyhacはPythonスクリプト(設定ファイル)を記述するのでもちろんデコレータも使えます。デコレータの説明はdecorator.htmlが分かりやすかったので、こちらを見ていただくとして早速コードの紹介。

■ 処理時間計測

上記のサイトで紹介されていたのをちょこっといじったのが、最初に紹介する処理時間計測をするデコレータ。

・デコレータ
from keyhac import *

## 処理時間計測のデコレータ
def profile(func):
    import functools

    @functools.wraps(func)
    def _profile(*args, **kw):
        import time
        timer = time.clock
        t0 = timer()
        ret = func(*args, **kw)
        print '%s: %.3f [ms] elapsed' % (func.__name__, 1000 * (timer() - t0))
        return ret
    return _profile

↑ をconfig.pyの先頭で定義しておく。そして、以下のように測りたい関数の直前に@profileを挿入する。

・使用サンプル
def configure(keymap):

    ## メモ帳を起動
    @profile
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_notepad

するとこの関数が実行されたとき(=Win+Nが押されたとき)にkeyhacのコンソール画面に以下のような情報が出力されるという仕掛け。

exe_notepad: 352.611 [ms] elapsed

新しく関数を追加するときなどに、こうした方が処理は速くなるかな?ってときにお手軽に時間計測できます。機能は地味だけどとても便利。

■ サブスレッド処理

つぎはサブスレッド化ってやつを簡単にするデコレータ。処理に時間がかかる関数は、サブスレッド処理で実行することが推奨されているようです。

・デコレータ
from keyhac import *

## JobQueue/JobItem でサブスレッド処理にするデコレータ
def job_queue(func):
    import functools

    @functools.wraps(func)
    def _job_queue(*args, **kw):

        num_items = JobQueue.defaultQueue().numItems()
        if num_items:   # 処理待ちアイテムがある場合は、その数を表示
            print u"JobQueue.defaultQueue().numItems() :", num_items

        def __job_queue_1(job_item):
            return func(*args, **kw)

        def __job_queue_2(job_item):
            # print "job_queue : ", func.__name__, args, kw
            pass

        job_item = JobItem(__job_queue_1, __job_queue_2)
        JobQueue.defaultQueue().enqueue(job_item)

    return _job_queue
・使用サンプル

メモ帳起動関数をサブスレッド処理化。(関数定義の前に@job_queueを付ける)

    @job_queue
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

もし ↑ をデコレータ使わずに書いたら ↓ になる。

    def exe_notepad():
        def _exe_notepad_1(job_item):
            shellExecute( None, None, "notepad.exe", u"", u"" )

        def _exe_notepad_2(job_item):
            pass

        job_item = JobItem(_exe_notepad_1, _exe_notepad_2)
        JobQueue.defaultQueue().enqueue(job_item)

1つ2つなら良いが、毎回この処理を書くのが面倒になってきて、デコレータを使うようにしたらすごく楽チンになった。

■ キー定義

3つ目はキー定義。このキーが押されたらこの関数の実行するというのを関数定義と一緒に行う。

・デコレータ
from keyhac import *

## キー定義用デコレータ
def def_key(window_keymap, key):
    def _def_key(func):
        window_keymap[key] = func
    return _def_key
・使用サンプル

このデコレータは、@def_key(ウィンドウキーマップ, キー表記文字列) という風に使う。

def configure(keymap):

    keymap_global = keymap.defineWindowKeymap()

    ## メモ帳を起動
    @def_key(keymap_global, "W-n")  # Win + N
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

これは以下のコードと同等になる。

def configure(keymap):

    ## メモ帳を起動
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_notepad  # Win + N

このデコレータではコード記述量は減らないが、関数定義とキーへの紐付けを同時にできるのでコードがシンプルで分かりやすくなる。

■ 関数を返す

4つ目は関数を返す関数にするデコレータ。引数をともなう関数をキー割り当てするときには、関数を返す関数にする必要がある。それが面倒になって作ったのがこのデコレータ。

・デコレータ
from keyhac import *

## 関数を返す関数にするデコレータ
def ret_func(func):
    import functools

    @functools.wraps(func)
    def _ret_func(*args, **kw):
        @functools.wraps(func)
        def __ret_func():
            return func(*args, **kw)
        return __ret_func
    return _ret_func
・使用サンプル

関数定義の前に@ret_funcを挿入する。

def configure(keymap):

    # 引数のパスを起動する
    @ret_func
    def exe_sub(path):
        shellExecute( None, None, path, u"", u"" )

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_sub("notepad.exe")

これは以下のコードと同等になる。

def configure(keymap):

    # 引数のパスを起動する(デコレータ無し版)
    def exe_sub(path):
        def _exe_sub():
            shellExecute( None, None, path, u"", u"" )
        return _exe_sub

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["W-n"] = exe_sub("notepad.exe")

■ さいごに

とりあえずこんなところで。デコレータおもしろい。というかPythonおもしろいです。また何かあったら追加しときます。

追記(2011.08.02)

■ キー定義(別パターン)

3つ目のキー定義の亜種。このサイトの@addtoを使ってwindow_keymapを呼び出し可能にしてしまう。上で書いたやつよりシンプルでいいかもしれない。

・デコレータ
from keyhac import *

# http://wiki.python.org/moin/PythonDecoratorLibrary#Easy_adding_methods_to_a_class_instance
def addto(instance):
    def decorator(f):
        import types
        f = types.MethodType(f, instance, instance.__class__)
        setattr(instance, f.func_name, f)
        return f
    return decorator

def configure(keymap):

    def define_window_keymap(*args, **kw):
        window_keymap = keymap.defineWindowKeymap(*args, **kw)
        print args, kw

        @addto(window_keymap)
        def __call__(self, key):
            def def_key(func):
                self[key] = func
            return def_key

        return window_keymap
・使用サンプル
    # keymap.defineWindowKeymap の代わりに define_window_keymap を使う
    keymap_global = define_window_keymap()

    # 通常どおりのキー設定も可能
    keymap_global["S-C-w"] = "LWin"

    # デコレータにもなる
    @keymap_global("W-n")
    def exe_notepad():
        shellExecute( None, None, "notepad.exe", u"", u"" )

■ 参考リンク

keyhacからcomtypes経由でExcelマクロ関数をコールする

前回に引き続き、keyhacネタ。keyhacはPythonコードでWindowsのショートカットキーをカスタマイズできるフリーソフト。バージョン1.34からcomtypesが使用できるようになった。
このおかげでWScriptでやれていたことがkeyhacでもできるようになった。下記コードのようにすればExcel側で定義している自作マクロ関数も呼び出せる。

from keyhac import *


def configure(keymap):

    def personal_xls(macro_name, *args):
        def _job_1(job_item):
            import comtypes.client
            xl = comtypes.client.GetActiveObject("Excel.Application")

            is_older_than_2007 = lambda: float(xl.Version) < 12
            if is_older_than_2007():
                book_name = "PERSONAL.XLS"
            else:
                book_name = "PERSONAL.XLSB"

            xl.Run(book_name + "!" + macro_name, *args)

        def _job_2(job_item):
            # print macro_name, args
            pass

        job_item = JobItem(_job_1, _job_2)
        JobQueue.defaultQueue().enqueue(job_item)

    # Excel用キーマップ
    keymap_excel = keymap.defineWindowKeymap(exe_name=u"EXCEL.EXE")
    # ボタンを大きくするマクロ
    keymap_excel["C-S-b"] = lambda: personal_xls("SwitchLargeButtons")
    # 引数付きの関数もコール可能!
    keymap_excel["C-S-m"] = lambda: personal_xls("Macro1", u"abcde.")
    keymap_excel["C-S-n"] = lambda: personal_xls("Macro1", u"あいうえお!")

一応Excel2003も2007も動くようにしている。
それとSwitchLargeButtonsは前に書いたやつ → 一発でエクセルのボタンをビッグサイズに切り替えるマクロ
ちなみにこのコードではJobQueueを使っているが、これを使わずに直に関数コールするとエラーにになってしまう。

ERROR : _onKeyDown failed
[Error -2147417843] アプリケーションが入力同期呼び出しをディスパッチしているため、呼び出せません。
Traceback (most recent call last):
  File "keyhac_keymap.pyo", line 804, in _onKeyDown
  File "keyhac_keymap.pyo", line 728, in _keyAction
  File "C:\Program Files\craftware\keyhac\config.py", line 1764, in <lambda>
  File "C:\Program Files\craftware\keyhac\config.py", line 1747, in personal_xls
  File "comtypes\client\__init__.pyo", line 180, in GetActiveObject
  File "comtypes\__init__.pyo", line 1165, in GetActiveObject
  File "_ctypes/callproc.c", line 936, in GetResult
WindowsError: [Error -2147417843] アプリケーションが入力同期呼び出しをディスパッチしているため、呼び出せません。

Excelは無理なのかなと色々試してたら一応動いた。結果オーライ。ますますkeyhacが便利になったなー。

■ 参考リンク

PCで音楽再生中に一時停止し、ちょっとしたら自動再開させる方法

ゴールデンウィークあたりからkeyhacというソフトでのカスタマイズにハマっています。
これで何ができるかというと、ソフトの名前(キーハック)のとおりいろんなソフトのショートカットキーをハックできてしまうのです。
ショートカットキーといえばCtrl+CのコピーとかCtrl+Vの貼り付けが有名どころですが、これらのキーと同じようなやつをユーザー側で定義して、勝手にショートカットキーを後付けしてしまおうというソフトな訳です。
で、その定義方法はLL言語Pythonで記述するようになっています。これが良い。すごく良い。
Pythonはまだ始めたばかり(条件文や関数のあとの:をしょっちゅう忘れるくらい)ですが、基本的にシンプルな言語なので書いてて楽しい。この処理よく使うな、よし定義だ、みたいな。
で、前置きが長くなったんですが、「Windows Media Playerとかで音楽再生中に一時停止し、5分したら自動再開させる」という「Alt+Shift+P」で実行させるためのスクリプト

from keyhac import *

def configure(keymap):

    def music_pause_and_play():

        def job_music_pause_and_play(job_item):
            import time
            keymap.command_InputKey("(%d)" % VK_MEDIA_PLAY_PAUSE)()
            time.sleep(5 * 60)  # [sec]
            keymap.command_InputKey("(%d)" % VK_MEDIA_PLAY_PAUSE)()

        job_item = JobItem(job_music_pause_and_play)
        JobQueue.defaultQueue().enqueue(job_item)

    keymap_global = keymap.defineWindowKeymap()
    keymap_global["Alt-Shift-P"] = music_pause_and_play

ちなみに

            keymap.command_InputKey("(%d)" % VK_MEDIA_PLAY_PAUSE)()

の部分は、以下のように書き換えても同様に動作します。(多分 ↓ こっちの方が処理は軽い)

            pyauto.Input.send([pyauto.Key(VK_MEDIA_PLAY_PAUSE)])

なお、上記のスクリプトは、『別の作業をして音楽を一時停止させた後、再生し忘れる人は必見のスクリプト | ライフハッカー[日本版]』でAutoHotkey*1を使った方法として紹介されていてナルホドな〜、と思ったのでkeyhacでもやってみました。
PCで音楽を聴いているところに電話がかかってきたときに便利ですね。
(とっさにAlt+Shift+Pは個人的にはちょっと押しにくい気もするけど)
そんな訳でkeyhacおすすめです。WindowsユーザーでPythonに興味のある方は是非。また何か思いついたら書きたいと思います。

*1:keyhacと同様のショートカットキー定義ソフト、こっちの方が有名だが独自言語に馴染めなかった

超便利なChrome拡張のSmooth Gesturesのカスタムアクション設定方法

20110508173109
Google Chrome拡張機能であるSmooth Gestures。たぶん定番拡張だと思うのですが、カスタムアクション(bookmarkletでの機能追加)の設定方法について検索してみたのですが、公式のヘルプも無いし解説ページも見つからなかったので自力でなんとかしてみました。
以下そのメモです。

・設定方法

■ オプションを開く

[+カスタムアクションを追加]のボタンを押す
20110508173107

javascriptでコードを書く&その他設定

ブロックが追加されるので、各項目を設定します。とりあえず手始めに「選択文字列でGoogle検索」です。
20110508173106
1.「starting anywhere」を「有効な選択がされているときのみ」に変更
2.アクションの名前として「Google 検索」を入力
3.アクションの説明として「選択文字列で検索」を入力
4.javascriptのコードを入力 ↓

(function(url){
    window.open(url.replace("%s",encodeURIComponent(window.getSelection())));
})("http://www.google.co.jp/search?q=%s&num=100")

5:[save]ボタンを押す

ジェスチャーを設定する

他のアクションと同様に登録すればOKです。
20110508173108

これでOK!できてしまえば何てことはないですね。

・検索結果を背面のタブで開く場合は

それと上記のスクリプトだとアクション後に前面のタブで開くのですが、バックグラウンドのタブで開きたい場合は以下のようにすれば良いみたいです。

(function(url){
    var a=document.createElement("a");
    a.href=url.replace("%s",encodeURIComponent(window.getSelection()));
    var event=document.createEvent("MouseEvents");
    event.initMouseEvent("click",true,true,window,0,0,0,0,0,false,false,false,false,1,null);
    a.dispatchEvent(event);
})("http://www.google.co.jp/search?q=%s&num=100")

・他の検索も登録

ちなみに↓の★の部分を変更すれば、色々な検索エンジンも使えます。
(背面タブバージョンも同様です)

(function(url){
    window.open(url.replace("%s",encodeURIComponent(window.getSelection())));
})("★")

↑ の★を ↓ のURLに置き換える

検索エンジン URL
google
amazon.co.jp
amazon.deボードゲームamazon販売のみに限定)
■ play:game

ボードゲーム

はてなブックマーク
はてなブックマーク

(タグで検索)

英辞郎
クックパッド
twitter検索
価格.com (注意*1
■ -

これ以外にもChrome検索エンジン設定(chrome://settings/searchEngines)からURLを持ってくれば大体イケると思います。

・リンク上で開始するアクションの設定例

1.「starting anywhere」を「有効なリンクのみ」に変更
2.アクションの名前として「Twitter 検索」を入力
3.アクションの説明として「リンクを検索」を入力
4.javascriptのコードを入力 ↓

(function(url){
    window.open(url.replace("%s",encodeURIComponent(b.targets[0].href)));
})("http://search.twitter.com/search?q=%s")

リンク先のページについてTwitterでの評判が見られます。
ちなみにこれも最後のURL部分を他に変えることで流用が可能です。

・画像上で開始するアクションの設定例

1.「starting anywhere」を「有効な画像のみ」に変更
2.アクションの名前として「TinEye(類似画像検索)」を入力
3.アクションの説明として「画像を検索」を入力
4.javascriptのコードを入力 ↓

(function(url){
    window.open(url.replace("%s",encodeURIComponent(b.targets[0].src)));
})("http://tineye.com/search?pluginver=bookmark_1.0&url=%s")

・さいごに

前は別のマウスジェスチャの拡張を使っていましたが、思い切って乗換えてみました。まだ使い始めてから日が浅いので↑のコードも何か不具合があるかもしれませんが、いまのところは良い感じです。よければご参考に。

*1:価格.comのみコード部分のencodeURIComponent→escapeに変更の必要あり、しないと検索結果が文字化け

Notepad++のNppExecの出力からタグジャンプする設定方法

とりあえずできるようになったのでメモ。

メニューの プラグイン>NppExec>Execute... (F6)
コンパイラやコーディングチェッカを実行する

例. pep8 --show-source "$(FULL_CURRENT_PATH)"

するとConsoleというウィンドウにファイル名と行数が表示される。

C:\Program Files\keyhac\config.py:565:2: W291 trailing whitespace

↑こんな感じ(書式は実行するソフト等による)
これをダブルクリックすると、その行にジャンプするようにしたい。

設定

メニューの プラグイン>NppExec>Console Output Filters...
を選択し、HighLightタブで

%ABSFILE%:%LINE%:*

↑はConsoleに出力されるメッセージ書式に合わせる

%ABSFILE% フルパスのファイル名
%LINE% 行数

これでその行のチェックボックスをONにすれば設定完了。
ちなみにConsoleウィンドウの書式はAdvanced OptionsとChange Console Fontで変更可能。

検索結果の「Amazon.co.jpが販売」以外半透明化のgreasemonkey&bookmarklet

最近のAmazonに多いぼったくり転売屋に引っかからないための検索フィルター(greasemonkeyスクリプトbookmarkletを作ってみました。
まずは次のスクリーンショットを見ていただくのが早いと思います。発売間近のニンテンドー3DSで検索したときの画面です。

ビフォー

before

アフター(上から2つのアイテムが半透明になる)

after
ニンテンドー3DSは定価25000円なんですが、マーケットプレイスにて転売業者が現在37000円〜で販売しています。

インストール

好みでお好きな方をどうぞ!

greasemonkeyスクリプト( ↓ のリンクをクリック)



このUserscriptをインストールする

ブックマークレット( ↓ のリンクをブックマーク)

いまのところIEは非対応です。Google ChromeFirefoxOperaでは動作確認OK、AutoPagerize・AutoPatchWorkにも対応しています。

おまけ

こっちも最近つくったAmazonbookmarkletです。Amazonの商品ページで実行すると、そのカテゴリの売れ筋を表示します。