好きなエディタでFirefoxに入力

やりたいこと

ブラウザの入力エリアはとても貧弱で, とくに普段からEmacsのような高機能エディタを使っていたり, Emacsのような高機能エディタのviエミュレーションモードを使っていたりするような変態さんにとって, これは耐え難い苦痛.

JavaScriptを頑張って書いて入力エリアそのものを強化することも考えられるけれど, コストが大きい. なにか簡単な方法で, 外部のエディタとブラウザの間でやりとりさせられるならその方が楽. それを実現してみた, というお話.

具体的には, ある決められたファイルに何か書き込むと, それがブラウザのアクティブな入力エリアに書き込まれる, ということを実現する.

やり方

まずは雰囲気をつかむためにデモ動画を見てみよう.

Windowsの人

動画の通りにするだけで使える.

  1. FirefoxGreasemonkeyをインストール
  2. Windows用のインストーラをダウンロードしてインストール
  3. Greasemonkey用のユーザスクリプトをインストール
  4. デスクトップの「テキストを編集」ショートカットを好きなエディタで開く
Windows以外(Linuxとか)の人

詳細はGitHubのリポジトリを参照.

インストール
  git clone git://github.com/tarao/textserver.git
  cd textserver
  git submodule update --init
  mkdir some-dir
  echo ':dir: absolute-path-to-some-dir' > textserver.yml

リポジトリのコピーと必要なモジュールが入る.

Debian系の人はここで

  sudo aptitude install libinotify-ruby

しておくと少し幸せになる(これがないと100ミリ秒ごとにファイルの更新確認をするので非効率的, 詳細は技術的な話を参照).

起動
  ruby textserver.rb -c textserver.yml

とするとデーモンが起動. あとはFirefoxGreasemonkey用のユーザスクリプトをインストールすれば使えるようになる. 具体的には, some-dir/text というファイル(デーモンを動かすと勝手にできるはず)を編集して保存するとブラウザの入力エリアに書き込まれるようになる.

停止

デーモンを止めるときは

  ruby textserver.rb -c textserver.yml --stop

とする.

高度な設定

.ymlに設定可能な項目とデフォルト値は以下の通り.

:dir:      /tmp/textserver
:text:     text
:reset:    reset
:lock:     lock
:pid:      /tmp/textserver/textserver.pid
:logfile:  /dev/stdout
:loglevel: 0
:server:
  :BindAddress: 127.0.0.1
  :Port:        18080

別のホストでブラウザを動かす場合は:BindAddressを0.0.0.0にして, デーモンの動いているホストの名前hostnameを, Firefoxのプロファイルディレクトリにあるprefs.jsに

user_pref("greasemonkey.scriptvals.http://orezdnu.org//retrieve-remote-text.TextServer:host", "hostname");

と書き加えて設定する.

デフォルト以外(たとえば3000)の値を:Portに設定したときも, prefs.jsに

user_pref("greasemonkey.scriptvals.http://orezdnu.org//retrieve-remote-text.TextServer:textPort", 3000);

と書き加えて設定する. 実はデフォルト設定の場合も18080番と18081番の2つを使っているので, 2つ連続で空いているポートを指定すること.

Emacsとの連携

Emacs用のパッケージをload-pathのどこかに置いて, .emacs

(require 'textarea)
(setq textarea:dir "absolute-path-to-some-dir")

と書いておくと, M-x textareaでsome-dir/textを*textarea*バッファとして開くことができる. このバッファは数秒で自動保存される. さらに, M-x revert-bufferブラウザの入力エリアの内容をバッファに逆流させることができる. これはすごい!

技術的な話

基本的な仕組み

デーモンはテキストファイルを監視していて, ファイルに変更があったらブラウザ側にその内容を通知するというだけ.

HTTP long polling

仕組みの上ではブラウザがサーバで, ファイルに書き込んだ側がクライアントになるけれど, 実装上はファイルの変更を監視しているデーモンがサーバで, そのデーモンに対してXMLHttpRequestするブラウザ側がクライアントになる. そこで, ファイルに変更がない間はデーモン側がレスポンスを返さないようにすることで, デーモン側の都合でデータの流れるタイミングを制御している. いわゆるHTTP long polling.

ファイルの監視

ファイルの監視をするのに, 無限ループして数ミリ秒ごとにファイルの最終更新日時を調べるような実装はなんだかキモい. Windowsの場合はwin32-changenotifyを使ってファイルの更新チェックをOSに任せてしまって, 更新があるまでOSから制御が返ってこないようにしている. libinotify-rubyを入れておくと, Linuxの場合にもinotifyというカーネルのサブシステムを使って似たような挙動をするようになる.

追記

It's All Text!との違い

textserverでできてIt's All Text!でできないこと:

  • キーボードだけで編集する
  • 既に起動しているエディタで編集する
  • 入力エリアで発生した変更を任意のタイミングでエディタに逆流する
  • ブラウザとは別のホストで動いているエディタで編集する
2013-01-20T16:50+0000
  • Emacs用のパッケージを書き直したので設定方法を修正