先日、Emacsのgrepモードの説明をしました。これの応用として、JavaScriptの構文チェッカーであるJSLintをEmacsから使ってみます。
Windows上のMeadow3での話ですが、Windows特有の部分は、他のOSでは問題にならないゴタゴタのところ(例:危険な^Z)だけです。
内容:
- Emacsのjavascriptモード
- JSLint
- Rhino
- とりあえず動かしてみる
- JSLintソースの修正
- EmacsからJSLintを使う
- MakefileからJSLintを使う
●Emacsのjavascriptモード
[追記]
javascriptモードは推奨できません。「EmacsでJavaScriptソースを快適に読むために:js2-modeとエグズーベラントCtags」 をご覧ください。
[/追記]
- http://www.brgeight.se/ (2008-04時点)
.emacsの設定例:
;; Author: Karl Landstrom
;; Maintainer: Karl Landstrom
;; Version: 2.0 Beta 8
;; Date: 2006-12-26
;; javascript-mode
(add-to-list 'auto-mode-alist
(cons "\\.\\(js\\|json\\)\\'" 'javascript-mode))
(autoload 'javascript-mode "javascript" nil t)
(setq js-indent-level 2) ; 値はお好みにより
●JSLint
JSLintには、Konfabulator版、WSH版、Rhino版があります。ここでは、Rhino版について説明します。
Rhino版jslint.jsをhttp://www.jslint.com/rhino/index.htmlからダウンロードできますが、これは改行や空白を取り除いたもので、人間が見るには辛すぎます。後で少し変更する都合があるので、JSLintページ一番下のImplementationのところから普通のソースコードをダウンロードします。fulljslint.jsとrhino.jsの2つのファイルが必要です。
(コメントにjslint.jsと書いてありますが、ファイル名はfulljslint.jsです。)
// jslint.js
// 2008-04-09
/*
Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
// rhino.js
// 2007-02-19
/*
Copyright (c) 2002 Douglas Crockford (www.JSLint.com) Rhino Edition
●Rhino
現時点(2008-04)での最新版は Rhino 1.7R1 2008-03-06 rhino1_7R1.zip です。実行に必要なのはjs.jarだけですから、js.jarをどっか適当な場所に置きます。
次は、僕が使っている起動用バッチファイルです*1。
@echo off
REM rhino-c.bat -- コマンドライン版Rhinoif "%RHINO_HOME%" == "" set RHINO_HOME=C:\installed\rhinolib
if "%JS_JAR%" == "" set JS_JAR=%RHINO_HOME%\js.jarjava -cp %JS_JAR% org.mozilla.javascript.tools.shell.Main %1 %2 %3 %4 %5 %6 %7 %8 %9
●とりあえず動かしてみる
次のようにして自分用のjslint.jsを作ります。
または、
cat fulljslint.js rhino.js > jslint.js
ここで、/bを付けないと、最近の若い人はたぶん知らない^Zが末尾に付きハマリます。
copy /b fulljslint.js + rhino.js jslint.js
チェックしたいJavaScriptファイルを準備して、JSLintで調べてみます。ここでは、以前に掲載したRingBuffer.jsにツッコミドコロを後から入れたソースを使います。
>rhino-c jslint.js RingBuffer.js
Lint at line 7 character 32: Missing semicolon.
throw "Too small buffer size"Lint at line 10 character 21: Use the array literal notation [].
this.buffer = new Array(size);Lint at line 16 character 2: Missing semicolon.
}Lint at line 19 character 24: Line breaking error ')'.
return (this.next + 1)
この出力形式では、「grepコマンドとEmacs grepモードって、やっぱり便利だよな」で紹介したEmacsの便利な機能を使えないので、jslint.jsに手を加えます。
>copy jslint.js jslint.js.orig(この後、jslint.jsを編集)
JSLintでもう1つ困ったことは、JavaScriptソースコードに日本語の文字(UTF-8です)を使うと、それがコメント内であっても"Unsafe character."と文句を言うことです。 この点も修正します。
●JSLintソースの修正
出力形式に関しては、もともとrhino.js(rhino.jsはわずか36行)に含まれていた出力部分を書き換えればOKです。
Unsafe characterの判定は、cxという変数に含まれる正規表現に基づいてなされます。
// unsafe character
cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
このcsを書き換えればいいかなと思ったのですが、どう書き換えればいいか僕は分からなかったので、(望ましくはないけど)次のチェック部分を削ってしまいました。
s = lines[line].replace(/\t/g, ' ');
at = s.search(cx);
if (at >= 0) {
warningAt("Unsafe character.", line, at);
}
以前決めたパッチの作り方に従って、次のコマンドを実行します。
diff -u jslint.js.orig jslint.js > jslint.js.20080411.diff
パッチファイルは次のとおりです。このオリジナルjslint.jsは、fulljslint.jsとrhino.jsをアペンドしたファイルであることに注意してください。
--- jslint.js.orig Fri Apr 11 10:44:31 2008
+++ jslint.js Fri Apr 11 11:42:39 2008
@@ -644,17 +644,19 @@
// Private lex methods
function nextLine() {
- var at;
+// var at; //omitted by M.Hiyama
line += 1;
if (line >= lines.length) {
return false;
}
character = 0;
s = lines[line].replace(/\t/g, ' ');
+/* //omitted by M.Hiyama
at = s.search(cx);
if (at >= 0) {
warningAt("Unsafe character.", line, at);
}
+*/
return true;
}
@@ -3908,7 +3910,8 @@
for (var i = 0; i < JSLINT.errors.length; i += 1) {
var e = JSLINT.errors[i];
if (e) {
- print('Lint at line ' + (e.line + 1) + ' character ' +
+/* modified by M.Hiyama */
+ print(a[0] + ':' + (e.line + 1) + ': character ' +
(e.character + 1) + ': ' + e.reason);
print((e.evidence || '').
replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"));
@@ -3916,7 +3919,9 @@
}
}
} else {
+/* //omitted by M.Hiyama
print("jslint: No problems found in " + a[0]);
+*/
quit();
}
})(arguments);
●EmacsからJSLintを使う
パッチ済みのjslint.jsを適当な場所にコピーして、例えば次のようなバッチファイルを作っておきます。
@echo off
REM jslint-r.bat -- JSLint Rhino版if "%JSLINT_JS%" == "" set JSLINT_JS=%HOME%\lib\jslint.js
call rhino-c "%JSLINT_JS%" %1
Emacsから M-x compile として、Compile command にjslint-r RingBuffer.js
を指定すると、次のようになります。
(原寸大)
Meadowの場合、シェルとしてCygwinやMSYSのシェルを指定していると、バッチファイルをうまく実行できない(command not found)ときがあるので注意してください。次の式を評価すれば、Windowsバッチファイルを実行できる状況になります。
(setq explicit-shell-file-name "cmdproxy.exe")
(setq shell-file-name "cmdproxy.exe")
(setq shell-command-switch "/c")
●MakefileからJSLintを使う
僕は、シェルから直接ではなくてMakefileからJSLintを起動しています。
OK_FILES:=$(patsubst $(SRC_DIR)/%.js, $(BIN_DIR)/%.ok,$(SOURCES))
# ...lint: $(OK_FILES)
$(OK_FILES): $(BIN_DIR)/%.ok : $(SRC_DIR)/%.js
@echo "$< --> $@"
$(JSLINT_CMD) $< | tee $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint
@if [ ! -s $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint ]; then \
rm $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint ; \
touch $@ ; \
fi
ウーム、Makefileに凝っていた頃書いたやつだから、無駄に複雑だ。
わかんねぇっ。