Atom Editor で init.js を保存したら自動的に再読み込みする

テキストエディタ Atom では Emacs における init.el のように、~/.atom/init.js (init.coffee) にスクリプトを記述することによって自前のコマンドを定義できる。しかしながら Emcas のように load-file のようなコマンドが用意されていないため、書いたスクリプトを試すにはエディタの再起動が必要となる。

過去の方法

ここにファイルの保存を監視し require のキャッシュを消去する方法で実現する方法が紹介されている。

ところが2017年12月現在のバージョン 1.23.1 (x64) ではこの方法は使えなかった。 require のキャッシュを消去できないのである。2015年10月の情報だからだいぶシステムが変更されているのだろう。

別の方法

キャッシュを消去できないのであれば別のファイルを作ればいい。そう思い付き、次のような init.js を書いた。

var scriptId = 0;
var fnCache = {};

function reloadInitScript() {
	let srcFile = require.resolve("./siteinit"),
	destFile = srcFile.replace(/[.]js$/, "-cache/" + (scriptId + 1) + ".js");

	try {
		var fs = require("fs");
		let rs = fs.createReadStream(srcFile),
		ws = fs.createWriteStream(destFile);
		ws.on("close", () => {
			scriptId++;
			require("./siteinit-cache/" + scriptId).init();
		})
		rs.pipe(ws);
	} catch (error) {
		atom.notifications.addFatalError("Error on copying file to " + destFile);
	}
}

function defun(target, name, fn) {
	var fnKey = target + "\\" + name;
	if (!fnCache[fnKey]) {
		atom.commands.add(target, reffun(name, fnKey));
	}
	fnCache[fnKey] = fn;
}

function reffun(name, fnKey) {
	var cmd = {};
	cmd[name] = function() {
		fnCache[fnKey]();
	};
	return cmd;
}

Object.defineProperty(global, "user", {
	value: {
		defun: defun
	}
});

reloadInitScript();

atom.workspace.observeTextEditors(function(editor) {
	editor.onDidSave(function() {
		if (/[/\\][.]atom[/\\].+[.]js$/.test(editor.getPath())) {
			reloadInitScript();
		}
	});
});

このスクリプトの要点は次のようなところだ。

  • ユーザが編集するファイルは ~/.atom/siteinit.js とする
  • siteinit.js では user.defun() という函数でコマンドを定義する
  • fnCache というハッシュに函数の実体を保持しておき、 atom.commands.add() するのは実行時に fnCache に登録された関数を呼び出す函数
  • ファイルの保存を監視し、 ~/.atom/ 内の .js ファイルが更新されたら reloadInitScript() を呼び出す
  • reloadInitScript() が呼ばれるたびに scriptId を 1 ずつ増やしていき、 ~/.atom/siteinit-cache/(scriptId).js という連番のファイルを作る
  • ~/.atom/siteinit-cache/(scriptId).js を require し、その中の init() を呼び出す

そして siteinit.js は次のように使用する。

function init() {
	console.log("siteinit.js loaded.");

	user.defun("atom-text-editor", "user:insert-horizontal-bar", function() {
		let editor = atom.workspace.getActiveTextEditor();
		if (!editor) {
			return;
		}
		editor.insertText("----------------------------------------------------------------");
	});
}

exports.init = init;

init.js および siteinit.js を作成したら動作確認を行おう。

  1. Atom を再起動してから Ctrl+Shift+I でコンソールを開く
  2. siteinit.js の "siteinit.js loaded." というメッセージを書き換え、保存する

ここでコンソールに書き換えたメッセージが表示されれば設定は完了。快適な Atom ライフが待っている。