greasemonkeyスクリプト開発をAptanaのEclipse Monkeyで便利にする

Greasemonkeyスクリプトを書く際、今まではAptanaでぼちぼち書いていたのですが、ふと思い立ってSubversionでバージョン管理したくなってしまいました。
すると、Aptanaでコード書いた後、テストのためにはGreasemonkeyスクリプトFirefoxのプロファイル*1以下に手動コピーする必要があり*2、これでは面倒!!!
というわけで、いろいろ調べて試行錯誤してみたところ、EclipseMonkeyで開発中のGreasemonkeyスクリプトのファイル保存時に、あわせてFirefoxのプロファイルへコピーすることができました。

使ったソフトとサービス
  1. Aptana StudioGreasemonkeyスクリプトを書きました)
  2. SubclipseAptanaSubversionを使うためのプラグイン)AptanaStudioで「Help」→「Software Updates」→「Find and Install」から追加します
  3. unfuddle(無料のSubversionホスティングサービス)

AptanaGreasemonkeyスクリプトを保存した際、Firefoxのプロファイルにもあわせてコピーする方法

  1. Greasemonkeyスクリプトを作るプロジェクトを追加します。
  2. 追加したプロジェクトに、「monkey」というディレクトリを作ります。
  3. 上記の「monkey」ディレクトリ内に、以下にあるEclipseMonkey用スクリプトを「imageLoaderAutoCopy.js」として配置します。なお、対象のGreasemonkeyスクリプトを「imageloader.user.js」、Greasemonkeyスクリプトの保存先プロファイルディレクトリを「C:/Documents and Settings/hogeUser/Application Data/Mozilla/Firefox/Profiles/e7hv2lql.default/gm_scripts/」として記載してあります。ご使用の環境に合わせてSetting内を修正してください。また、プロファイルディレクトリの「e7hv2lql.default」の部分はランダムな文字列になっていますので、これもご使用の環境に合わせてください。
/*
 * Menu: GM > Save userscript Copy to FF2 profile.
 * Kudos: tomo.snowbug
 * License: EPL 1.0
 * DOM: http://download.eclipse.org/technology/dash/update/org.eclipse.eclipsemonkey.lang.javascript
 * Listener: commandService().addExecutionListener(this);
 */
var Setting = {
    copyFromFileName: "imageloader.user.js",
    copyToDir: "C:/Documents and Settings/hogeUser/Application Data/Mozilla/Firefox/Profiles/e7hv2lql.default/gm_scripts/"
};

/**
 * Returns a reference to the workspace command service
 */
function commandService(){
    var commandServiceClass = Packages.org.eclipse.ui.commands.ICommandService;
    var commandService = Packages.org.eclipse.ui.PlatformUI.getWorkbench().getAdapter(commandServiceClass);
    return commandService;
}

/**
 * Called before any/every command is executed, so we must filter on command ID
 */
function preExecute(commandId, event){
}

/* Add in all methods required by the interface, even if they are unused */
function postExecuteSuccess(commandId, returnValue){
    // if we see a save command
    if (commandId == "org.eclipse.ui.file.save") {
        execCopy();
    }
}

function notHandled(commandId, exception){
}

function postExecuteFailure(commandId, exception){
}

/*
 * execute [Scripts] -> [GM] -> [Save userscript Copy to FF2 profile.]
 */
function main(){
	execCopy();
}

function execCopy(){
    var filename = getActiveEditorsFileName();
    Logger.debug("call GMAutoCopy :" + filename);
    var targetFileName = Setting.copyFromFileName;
    var copyToDir = Setting.copyToDir;
    var targetFileFullPath = copyToDir + targetFileName;
    if (filename != targetFileName) 
        return;
    var file = new File(targetFileFullPath);
    file.createNewFile();
    file.write(editors.activeEditor.source);
    //showDialog("execute copy :" + targetFileFullPath);
    Logger.info("execute copy :" + targetFileFullPath);
}

function getActiveEditorsFilePath(){
    var filePath = editors.activeEditor.uri;
    return filePath.replace("file://", "");
}

function getActiveEditorsFileName(){
    var filePathArray = getActiveEditorsFilePath().split("/");
    return filePathArray[filePathArray.length - 1];
}

function showDialog(str){
    Packages.org.eclipse.jface.dialogs.MessageDialog.openInformation(window.getShell(), "INFO", str);
}

var Logger = {
    _console: null,
    loglevel: 0, //0=debug, 1=info, 2=warn, 3=error
    debug: function(str){
        if (this._console == null) 
            this._console = this._prepareConsole();
        if (this.loglevel < 1) 
            this._console.println(this._logFormat("DEBUG", str));
    },
    info: function(str){
        if (this._console == null) 
            this._console = this._prepareConsole();
        if (this.loglevel < 2) 
            this._console.println(this._logFormat("INFO ", str));
    },
    warn: function(str){
        if (this._console == null) 
            this._console = this._prepareConsole();
        if (this.loglevel < 3) 
            this._console.println(this._logFormat("WARN ", str));
    },
    error: function(str){
        if (this._console == null) 
            this._console = this._prepareConsole();
        if (this.loglevel < 4) 
            this._console.println(this._logFormat("ERROR", str));
    },
    
    _prepareConsole: function(){
        var consoleName = "Greasemonkey Auto Copy"
        var ConsoleMgr = Packages.org.eclipse.ui.console.ConsolePlugin.getDefault().getConsoleManager();
        var nowConsoles = ConsoleMgr.getConsoles();
        for (var i in nowConsoles) {
            if (nowConsoles[i].getName() == consoleName) 
                return nowConsoles[i].newMessageStream();
        }
        var MessageConsole = Packages.org.eclipse.ui.console.MessageConsole;
        var console = new MessageConsole(consoleName, null);
        var consoles = java.lang.reflect.Array.newInstance(MessageConsole, 1)
        consoles[0] = console
        ConsoleMgr.addConsoles(consoles);
        return console.newMessageStream();
    },
    _logFormat: function(level, str){
        var now = new Date();
        var h = this._heading;
        var formattedDate = "[" + now.getFullYear() + "/" + h(now.getMonth() + 1, 2) +
        "/" +
        h(now.getDate(), 2) +
        " " +
        h(now.getHours(), 2) +
        ":" +
        h(now.getMinutes(), 2) +
        ":" +
        h(now.getSeconds(), 2) +
        "." +
        h(now.getMilliseconds(), 3) +
        "]";
        return formattedDate + " " + level + " " + str;
    },
    _heading: function(str, length){
        var heading = "";
        var amount = length - new String(str).length;
        for (var i = 0; i < amount; i++) {
            heading += " ";
        }
        return heading + str;
    },
}

このEclipseMonkeyスクリプトについての説明

先頭にある、コメント部分にポイントがあります。

    • この部分は「Scripts」メニュー以下にこのスクリプトを登録します。
/*
 * Menu: GM > Save userscript Copy to FF2 profile.
 * ・・・
 */

こんな感じ。



    • 以下の部分がキモで、Aptana上で保存などを行った際に、このスクリプトが呼ばれるためのリスナーを登録しています。]
/*
 * ・・・
 * Listener: commandService().addExecutionListener(this);
 */

この登録のおかげで、以下の部分の処理が行われます。

function postExecuteSuccess(commandId, returnValue){
    // if we see a save command
    if (commandId == "org.eclipse.ui.file.save") {
        execCopy();
    }
}

ファイル保存時の処理を変えると他にも応用できそうです。

ところで

EclipseMonkeyって情報少ない?
editorsなどの定義済みなオブジェクト一覧とか、サンプルがもっとあるといいんですが・・・

*1:C:/Documents and Settings/(ユーザ名)/Application Data/Mozilla/Firefox/Profiles/(ランダムな名前)/gm_scripts/

*2:Aptanaのプロジェクトをgm_scriptsフォルダに作ってしまうと、他のUserscriptも一緒のプロジェクト内に入ってしまいます。複数のプロジェクトをひとつのディレクトリ内に作れないので、これではSubversionでの管理上よろしくありません。。。