mmm-modeでmajor-modeとsubmodeを切り替えるelispを書いた

mmm-modeを入れて結構たちましたが、ここ数日、ようやくphpやらhtmlやらjavascriptやらが混在したコードを書く機会がありました。
しかし、この質問の方と同じくmmm-modeで実際に書いてみるとインデントやらなんやらで全てを思い通りに動かせなかったので、
いろいろ設定を見直しつつ、elispの書き方を調べながら、簡単にモードを切り替えれるコードを書いたので載せて置きます。まだまだ使っていて問題があるかもしれませんが。

個々のモードの細かい設定は省いて、必要そうなところだけ抜粋

(add-hook 'xml-mode-hook
          '(lambda ()
            (setq mmm-classes '(html-others)) ;; 使用するmmm-add-groupの設定
            (mmm-mode)))

;; mmm-mode
(require 'mmm-auto)
(setq mmm-global-mode 'maybe)
(setq mmm-submode-decoration-level 2)
(invert-face 'mmm-default-submode-face t)
(setq mmm-font-lock-available-p t)
(mmm-add-group
 'html-others
 '((php-output
    :submode php-mode
    :front "<\\?php *echo "
    :back "\\?>"
    :include-front t
    :front-offset 5
    :insert ((?e php-echo nil @ "<?php" @ " echo " _ " " @ "?>" @)))
   (php-code
   :submode php-mode
   :front "<\\?\\(php\\)?"
   :back "\\?>"
   :insert ((?p php-section nil @ "<?php" @ " " _ " " @ "?>" @)
            (?b php-block nil @ "<?php" @ "\n" _ "\n" @ "?>" @)))
   (css-code
    :submode css-mode
    :delimiter-mode nil
    :front "<style[^>]*>"
    :back "</style>")
   (css-inline
    :submode css-mode
    :front "\\bstyle=\\s-*\""
    :back "\"")
   (js-code
    :submode javascript-mode
    :delimiter-mode nil
    :front "<script\[^>\]*\\(language=\"javascript\\([0-9.]*\\)\"\\|type=\"text/javascript\"\\)\[^>\]*>"
    :back"</script>"
    :insert ((?j js-tag nil @ "<script type=\"text/javascript\">"
                 @ "\n" _ "\n" @ "</script>" @)))
   (js-inline
    :submode javascript-mode
    :delimiter-mode nil
    :front "on\\w+=\""
    :back "\"")
   ))


;; mmmで基本となるmajor-modeの設定する
(setq switch-mmm-major-mode 'xml-mode)
;; major-modeとsubmodeを切り替える
(defun switch-mmm-major-sub ()
  (interactive)
  (if (and (boundp 'mmm-current-submode)
           (not (eq mmm-current-submode nil)))
      (funcall mmm-current-submode)
    (funcall switch-mmm-major-mode)
    (mmm-mode-on)
    (mmm-parse-buffer))
  )
(add-hook 'mmm-mode-hook
          '(lambda ()
             (define-key mmm-mode-map "\C-cm" 'mmm-parse-buffer))) ;; バッファをparseしなおす
(define-key global-map "\C-cl" 'switch-mmm-major-sub) ;; モードを切り替える

switch-mmm-major-sub()という関数で、mmm-modeが現在実行しているsubmodeに切り替えます。
例えばモードの表示が、XML[PHP] MMMとなっているときはPHP, XML[JavaScript] MMMとなっているときは上記関数でJavaScriptモードに切り替えます。もう一度 switch-mmm-major-sub()を呼ぶと、switch-mmm-major-modeで設定したモードに戻ります。(自分の場合はPSGMLのxml-mode)

上記の設定だと例えば

といった感じで切り替わります*1。この関数を好きなキーに当てておけば即座に切り替えれて便利です。

後、コードを書いている途中で<?php ?>内なのにsubmodeがPHPにならない!とかが結構頻繁にあるので、その時はmmm-parse-buffer()を呼んでパースし直します。これもキーに割り当ててると便利です。

これプラスC-c % eとかのinsert機能を使えばかなり効率があがりました。

ちなみに上記で使用している各モードはこちら。

mmmの参考はこの辺


うーむ。最近.emacsがかなり太ってきた。

*1:htmlタグ内のon~でjavascript-modeとかstyle=でcss-modeとかも