javascript.el の挙動がおかしかったので調査,そして解決。

どうも挙動がおかしかったので調べてみた。

  var array = new Array("a", "b", "c", "ほげ");

こんな風に,マルチバイトな文字があると,「;」を入力したときや,
「,」を入力したときに,カーソルがどっかにいってしまう。
これでは,非常に不便なので,調べて治した
いや,たぶん治っているはずだ。きっと。おそらく。

結論

オリジナルのソースのおかしなインデントとか無駄な空白とかが,
気にいらんかったが放置したです。

--- javascript.el	2010/08/21 17:52:14	1.1
+++ javascript.el	2010/08/21 19:12:25	1.3
@@ -453,7 +453,7 @@
 	  (offset (- (current-column) (current-indentation))))
       (when (not (nth 8 parse-status))
 	(indent-line-to (js-proper-indentation parse-status))
-	(when (> offset 0) (forward-char offset))))))
+	(when (> offset 0) (move-to-column (+ offset (current-indentation))))))))
   
 
 ;; --- Filling ---

原因とか

javascript.el では,

(when javascript-auto-indent-flag
  (mapc (lambda (key) 
	  (define-key javascript-mode-map key 'javascript-insert-and-indent))
	'("{" "}" "(" ")" ":" ";" ",")))

によって,セミコロンやカンマは,javascript-insert-and-indent 関数に割り当てられているようです。
(javascript-auto-indent-flagがnilではない限り)


cc-modeなどでは,セミコロンなどを入力すると綺麗にインデントしてくれるので便利ですね。
おそらく,javascript-mode でも,それをやろうとしているんですが,
マルチバイト文字のことが考慮されていないようです。


javascript-insert-and-indent 関数は,indent-according-to-mode を呼ぶんですが,
これは,indent-line-function という変数が中身を握っていて,
今回の場合,javascript-indent-line がその実態です。
つまり,javascript-indent-line 関数を呼ぶということです。


でですね,これは何をしているかというと,よくわからんのですが,
現在のカーソル位置の周辺を色々パースして,indent-line-to関数を使い,
インデントしているようです。
しかしこの関数は,ポイントをインデントのケツに移動させるわけです。
例えば,M-m した所などがそのケツです。行の末尾ではないことに注意。


これをほったらかしにすると,どえりゃーうっとーしーことになるんで,
オフセットなどを計算して,何事も無かったようにポイントを移動させるわけです。
それを担当しているのが,javascript-indent-line 関数の中の,

(when (> offset 0) (forward-char offset))

という部分です。


問題は,カラム数と文字数は必ずしも一致しない,ということです。
アスキー文字を使用している分には,おそらく一致するのでしょうが,
日本語の場合は,1文字で2カラム使用するので,
オフセットをカラム数で計算して,forward-char しちゃうと,矛盾が生じるわけです。
それが今回の,カーソルがぶっとぶ現象の原因,なはず。


解決するまで,紆余曲折あったんですが,move-to-column という関数を見つられたので,
簡単に修正することができましたよっと。

まとめ

  • カラム番号と文字数を一緒くたにしてはいけない。
  • 日本語なめるな。
  • move-to-column 関数,万歳!


簡単なテストというかお試しはしましたが,まだバグっている可能性がありますので,
ご注意をば。
というか,こんなことしている暇あったら,
popup-global-mark-ring のテストを書けっていうの。
バグバグだらけだっちゅーの。