Hammerspoon と Karabiner Elements でキーカスタマイズした

新しいMacBook Proが届いたんだけど、Karabiner(旧 KeyRemapForMacBook)が動かない。そこで、Karabiner 使えない対策: Hammerspoon で macOS の修飾キーつきホットキーのキーリマップを実現する - Qiitaを参考にしながら、以前のMacBookでおこなっていた「Vim のインサートモードから抜ける時にIMEを英数に切り替える」というのをHammerspoonとKarabiner Elementsで実現した。

Karabiner Elementsで、EscをF13にリマップした後に、以下のHammerspoonのluaスクリプトを書いた。Control + Shift + ; はGoogle日本語入力で英数に切り替えるショートカット。

local function keyCode(key, modifiers)
   modifiers = modifiers or {}
   return function()
      hs.eventtap.event.newKeyEvent(modifiers, string.lower(key), true):post()
      hs.timer.usleep(1000)
      hs.eventtap.event.newKeyEvent(modifiers, string.lower(key), false):post()      
   end
end

local function keyCodeSet(keys)
   return function()
      for i, keyEvent in ipairs(keys) do
         keyEvent()
      end
   end
end

local function remapKey(modifiers, key, keyCode)
   hs.hotkey.bind(modifiers, key, keyCode, nil, keyCode)
end

remapKey({}, 'f13', keyCodeSet({
    keyCode('escape', {}),
    keyCode(';', {'ctrl', 'shift'})
}))

気持ちよく作業するために致し方ないのだけど、なんか面倒くさいね... Hammerspoon、luaスクリプトを書くことでウィンドウをリサイズしたり、メニューバーに何か出したりとか、結構色々なことが実現できるようなので、時間があったらもうちょっと遊んでみたい。

おまけで、上のスクリプトを書くために、入力しているキーを調べたくなって、以下のようなluaスクリプトも書いたので乗っけておく。モードを用意して、そのモードに切り替えると入力したキーの内容がアラートとして表示される。

local function showKeyPress(tapEvent)
    local charactor = hs.keycodes.map[tapEvent:getKeyCode()]
    hs.alert.show(charactor, 1.5)
end

local keyTap = hs.eventtap.new(
  {hs.eventtap.event.types.keyDown},
  showKeyPress
)

k = hs.hotkey.modal.new({"cmd", "shift", "ctrl"}, 'P')
function k:entered()
  hs.alert.show("Enabling Keypress Show Mode", 1.5)
  keyTap:start()
end
function k:exited()
  hs.alert.show("Disabling Keypress Show Mode", 1.5)
end
k:bind({"cmd", "shift", "ctrl"}, 'P', function()
    keyTap:stop()
    k:exit()
end)

Charlesの Map Local のバグ?を Map Remote で回避する

題の通りなんだけど、すぐに忘れそうなので個人用メモ。

Debugging Proxy の Charles には、Map Local という特定の通信のレスポンスボディをローカルファイルで置き換える機能がある。production のサーバーで配信している script や画像に問題があって、原因の特定をおこなうときに便利。

で、この Map Local、script でやっているとちょいちょい構文エラーが起きることがあって、調べてみると、やたら長い行があると途中でぶった切っているようだった。そんなやたら長い行になるのが問題なのでは、と突っ込まれそうだが、Browserify でビルドしたり、Uglifyをかけたりとやっていると1行が長くなることはよくある。

困って途方に暮れていたんだけど、Map Local していたファイルを配信するサーバーをローカルで適当な方法で立てて、そのサーバーに対して Map Remote すれば回避できることに気づいた。

天才 を読んだ

田中角栄の一人称で書かれている自伝的な小説。
読みやすいのだけど、田中角栄を取り上げた番組や記事に書かれているような逸話を取ってくっつけた印象があって、この人物が持っていたであろう凄みや底が見えない深さが感じられなかったのが残念。

天才

天才

Browserify を使ったプロジェクトでファイルサイズを大きくしているライブラリを探す

azuさんのQiitaの記事をなぞっただけなんですが、便利だったのでメモ。

きっかけ

Browserify でバンドルされるファイル(未圧縮)のサイズが気づいたらIMを超えていて、どのライブラリがボトルネックになっているのか調べることに(個人的にPCだと1M, SPだと300kを超えたらサイズを見直すようにしている)。

調べる手順

  • browserify の full-paths オプションを有効にしてビルドする。上の記事だとコマンドのオプションとして指定していますが、JS APIでも指定可能なので gulp や grunt plugin でも問題ない。
  • ビルドされたファイルを discify に食わせて、ブラウザで可視化された依存関係を眺めて、ボトルネックとなっているライブラリを探す。
    • cat bundle.js | discify —open

こんなかんじで可視化される(アニメーションでけっこう酔う) f:id:kitak:20161026123150p:plain

減らす

調べてみたら lodash がけっこうな割合を占めていたんですが、実際に使っている関数は片手で数えるぐらいだったのと、lodash は関数ごとにパッケージを分割してnpmに公開されているので( https://www.npmjs.com/browse/keyword/lodash-modularized )、lodash を丸ごと使わずに必要な関数のパッケージをインストールして使うことにしました(自分で書いちゃってもよさそうですが)。 これで約400k減らすことができました。

他にも core.js とか moment がけっこうな割合を占めているという印象。なんとかして減らせないかな。

世界のドキュメンタリー オバマのホワイトハウス を見た

見た。

www.nhk-ondemand.jp

4回に分けて、オバマ政権8年の軌跡を描くドキュメンタリー。 リーマンショック医療保険改革、中東の春、シリア問題、ISの台頭、銃規制、移民制度改革といった国内外の問題に大統領が立ち向かう様子を取り上げている。

演説の名手の民主党の大統領。リベラルな政策で保守派とぶつかり、下院の多数派が共和党になり妥協を強いられる。昔見た「The West Wing」というポリティカルな海外ドラマと重なる。巧みな言葉使いで人々をリードし、大胆かつ粘り強く物事を進めていく、本当に何かの映画かドラマを見ているような気持ちになるドキュメンタリーだった。

依存する npm パッケージのバージョンを上げ続けるために ci-npm-update を導入した

題の通りなんですが、導入。

プロジェクトが依存する npm パッケージのバージョンを上げるのを、忙しさにかまけてサボりがちになっていた。意識が低くてもいいかんじにできないかなぁ、と思っていたところ、ci-npm-update を見つけた。

GitHub - bitjourney/ci-npm-update: Keeps npm dependencies up-to-date, making pull-requests from CI

npm-shrinkwrap.json (Ruby の Bundler でいう Gemfile.lock に相当するもの) を再生成して差分があったら、GitHub に PullRequest を出してくれるコマンド。PullRequest には、パッケージのリポジトリCompare View の URL が書かれていて、バージョン間でどんな変更があったかすぐ確認できて便利。

今のところ、Jenkins で週に1回程度 ci-npm-update を実行するジョブを実行させて、差分を確認したり、自動化テストが通るか、手元で動かして問題ないか調べて、良さそうだったらマージ。その後、テストエンジニアの人が週に1回おこなっているリグレッションテストで問題がないか確認してもらっている。

ただ、package.json に書かれた範囲内でしかアップデートできないので、ライブラリのメジャーバージョンアップがあった場合は気づけないのが難点。それは半月に一回くらい、npm outdatednpm-check-updates の出力を通知したり、package.jsondependenciesdevDependencies を更新する PullRequest を出させると良いのかなぁ、とぼんやりと考えている。

JavaScript のメモリや GC を扱った記事のメモ

JavaScript のメモリ や GC を意識したプログラミングに関する記事をふたつばかり読んだので、個人用のメモ。

www.html5rocks.com

  • V8 が minor GC を実行するときは 10ms オーダーの時間がかかる。
  • 60fps を実現する場合の 1フレームは 16ms であるから、GC が頻発すると全体の fps が低下し、表示のかくつきや操作の違和感に繋がる。
  • GC が発生するのは大量のオブジェクトの生成・破棄を繰り返したときだから、事前にオブジェクトを生成しておいてそれを使い回せばいい。(オブジェクトプール)
  • 万能ではない。初期化に時間がかかるし、大してオブジェクトを作らない場合でもメモリを無駄に使用することになる。また、その無駄なメモリを解放するタイミングが難しい。
  • ( モバイルの場合は、大量のメモリの使用がバッテリーの著しい消費に繋がるから、滑らかな UI を実現することとのトレードオフな気がする )
  • (どこで聞いたか忘れたけども、React は生成したインスタンスをプールするutil関数を定義している react/PooledClass.js at 19b8eadb242054e2668cf90e83490ef552bcdbfe · facebook/react · GitHub )
  • ( 話がズレるが、↑のコードで引数の個数毎に関数を定義しているのは apply よりも call のほうがパフォーマンスが良いため )

www.html5rocks.com

  • V8 の GC の説明。世代別 GC、minor GCアルゴリズムはコピー GC で、major GCアルゴリズムはマークコンパクト GC
  • Gmail のパフォーマンス改善のために performance.memory API で情報の収集をおこなった。
  • Chrome DevTools でメモリリークの原因を特定できる。
  • 先の記事と同様で、GC の頻発は避けましょう。
  • ( ページを何日も開き続けるサービスに関わったことがないのだけど、1~2時間開き続けるサービスは心当たりがあるので、メモリの使用量を調べてみるとよさそう )