org-babel: HTMLエクスポートでコードハイライト用CSSを分離する

org-babel を使えるようにする

Changelog によれば org 6.31 から org-babel が同梱されるようになったとのことで、せっかくなので org ごと新しいものにしておく。
.emacs などで (require 'ob) などと書かなくても自動的にロードされ、org-babel の機能が使えるようになる(自分の環境ではそうでした)。

参考: Org-mode list of user-visible changesVersion 6.31 Org-babel is now part of the Org distribution

ドキュメントも org 本体に取り込まれたそうです。

HTMLエクスポートでコードハイライト用 CSS を分離

org-export-as-html などで普通に HTML にエクスポートすると、コードハイライトされるのは良いとして、スタイルが style属性で直に埋め込まれる。

<pre class="src src-emacs-lisp">
<span style="color: #4aa;">(</span>setq x 
<span style="color: #4aa;">(</span>
<span style="color: #f66;">+</span> 1 2<span style="color: #4aa;">)</span>
<span style="color: #4aa;">)</span>
</pre>

これを class にして分離するには
M-x customize-group RET org-export-htmlize RET
で org-export-htmlize-output-type を "css" にする(デフォルトは inline-css)。

または
(setq org-export-htmlize-output-type 'css)


こうなります:

<pre class="src src-emacs-lisp">
<span class="org-my-paren">(</span>setq x 
<span class="org-my-paren">(</span>
<span class="org-my-symbol">+</span> 1 2<span class="org-my-paren">)</span>
<span class="org-my-paren">)</span>
</pre>

EmacsのフェイスをCSSとして出力

各 class に割り当てるスタイルは一から自分で作ってもいいが、

M-x org-export-htmlize-generate-css

するとフェイスを元に生成してくれるので、これをいじるのが良さげ。

その他

  • font-lock-mode を有効にしておく。

org-mode を最新の安定版(7.4)に入れ替えた(2011-02)

      • -

apt で入ったものが古かったので新しいのに入れ替えてみる。
入れ替える前の org-mode のバージョンは
M-x org-version => Org-mode version 6.21b

最新の安定版は 7.4。

      • -

http://orgmode.org/index.html

から The standard distribution(org-7.4.tar.gz)をダウンロードして展開して出てきたフォルダを ~/elisp の下に置くとして、

の両方にパス(load-path)を通す。
contrib/lisp には htmlize.el が入っていて、これがないと HTMLエクスポート時にソースコードがハイライトされない。

;; .emacs に追記
(add-to-list 'load-path "~/elisp/org-7.4/lisp")
(add-to-list 'load-path "~/elisp/org-7.4/contrib/lisp")

これでインストール終わり。
M-x org-version => Org-mode version 7.4 となった。

      • -

古いものが
/usr/share/emacs/23.1/lisp/org/...
あたりに残っているけど、

  • load-path の優先順位に従って 7.4 の方を優先して読んでる(と思う)
  • 特に問題は起こってない(インストールしたばっかりだけど…)

ので、下手にいじらなくても問題ないんじゃないかと思いました。load-path 追加の 2行を消せばすぐ元の状態に戻せるし。

リージョン内の行数と文字数をモードラインに表示する(範囲指定している時だけ)

  • 「○○文字以内のきりのいい長さで文字列をコピーして別のところにペーストしたい」みたいなことがよくある
  • 文字数・行数をカウントするコマンド count-lines-region はあるが、カーソルを移動させて M-=、カーソルを移動させて M-=、 ... の繰り返しになってしまい、めんどくさい
  • カーソル移動に合わせて自動的に表示が更新されるようになってると楽でいい
  • リージョン選択していない時は表示しなくていい
(defun count-lines-and-chars ()
  (if mark-active
      (format "%d lines,%d chars "
              (count-lines (region-beginning) (region-end))
              (- (region-end) (region-beginning)))
      ;;(count-lines-region (region-beginning) (region-end)) ;; これだとエコーエリアがチラつく
    ""))

(add-to-list 'default-mode-line-format
             '(:eval (count-lines-and-chars)))

こんな風に表示されます:
2011-02-24 23:19:04

備考

  • 改行も 1文字としてカウントされる
  • mode-line-format はバッファローカル変数なのですべてのバッファで有効にするために set-default を……と思ったが、調べてたら default-mode-line-format というのが見つかって、せっかくなのでそっちを使った

org-mode の #+BEGIN_HOGE 〜 #+END_HOGE 入力支援

追記 2011-05-03: "


追記: yasnippet 使うといいよ、とコメントで教えてもらいました。下記参照。


M-x my-wrap-org-block するとミニバッファで入力を求められるので、ブロックのタイプ(EXAMPLE、SRC など、小文字でOK)を入力すると

  • リージョンが設定されている場合はそれを挟むように #+BEGIN_HOGE、#+END_HOGE を挿入
  • リージョンが設定されていない場合はカーソル位置に #+BEGIN_HOGE、#+END_HOGE を挿入
(defun my-wrap-org-block (input-str)
  (interactive "sInput block type: ")
  (let ((type (upcase input-str)))
    (if (and transient-mark-mode mark-active)
        (save-excursion
          (goto-char (region-end))
          (insert "#+END_" type "\n")
          
          (goto-char (region-beginning))
          (insert "#+BEGIN_" type "\n"))
      (progn
        (insert "#+BEGIN_" type "\n\n"
                "#+END_" type "\n")
        (forward-line -2)))))

とても再発明くさい。

Emacs Lisp の正規表現をちょっと見やすくする

はい、Emacs Lisp正規表現です。とても見づらいです。

emacs-lisp-regexp-face-1

しかし、よく見ると \ ( ) | のところだけフォントが太字になっているではありませんか! ひょっとしてそこだけ違うフェイスになっているのでは?

というわけで、 describe-face を使って調べてみたところ、次の 2つのフェイスが使われていることが分かりました。

  • font-lock-regexp-grouping-backslash
  • font-lock-regexp-grouping-construct

とりあえず色だけ変えてみました。

(set-face-foreground 'font-lock-regexp-grouping-backslash "#666")
(set-face-foreground 'font-lock-regexp-grouping-construct "#f60")

emacs-lisp-regexp-face-2

これだけでもだいぶマシになりますね!

anything-c-key-chord-describe をいじってみた

id:IMAKADO さんの

をちょこっと改造してみました。

もともとは key-chord-define-global を繰り返し書くのが冗長に感じて リスト+funcall の形にしたのですが、そしたらもうこのリストを使いまわせばよくね? と思ったのでそうしてみました。

↓こんな感じになります。
2011-01-02 22:32:23

(require 'key-chord)
(key-chord-mode 1)

(setq key-chord-list
      '(("fj"  . anything)
        ("^\\" . apropos)
        ("o0"  . howm-menu)
        ("\\]" . block-indent)
        ("@["  . toggle-truncate-lines)
        ("i9"  . describe-key-briefly)
        (";:"  . eval-expression)
        (";@"  . eval-region)
        (";l"  . eval-last-sexp)
        ("vg"  . view-mode)
        ("yu"  . moccur)
        ("dk"  . anything-c-key-chord-describe)
        ))
(dolist (pair key-chord-list)
  (funcall 'key-chord-define-global (car pair) (cdr pair)))

(defvar anything-c-source-key-chord-describe
  `((name . "key-chord describe bindings")
    (action . (("Call Interactively" . (lambda (c)
                                         (call-interactively
                                          (intern
                                           (car (cdr (split-string c " - ")))))))
               ("Add to kill-ring" . kill-new)))
    (candidates . (lambda ()
                    (mapcar
                     (lambda (pair)
                       (format "%s - %s" (car pair) (cdr pair)))
                     key-chord-list)))))

(defun anything-c-key-chord-describe ()
  (interactive)
  (anything '(anything-c-source-key-chord-describe)))
      • -

2011年 1月 3日 月曜日 18:59:08 JST
Call Interactively のとこが動かなくなっていたのを修正

追記 2011年 1月 4日 火曜日 21:41:51 JST

上のを適当に書いた後で Emacsテクニックバイブル の anything のとこをパラパラと見ていたら、335 ページに次のように書いてありました。


candidates 属性は、候補の取得方法を変数名かリストか無引数関数で指定します。変数の値、関数呼び出しの結果はリストである必要があります。リストの要素は文字列かシンボルを受け付けます。

上記のコードでは「文字列 or シンボル」以外のものを返していてダメな感じになってます。

また、real-to-display を使うと良さそうだということも分かり(同348ページ)、それも併せて次のように修正しました。

(defvar anything-c-source-key-chord-describe
  `((name . "key-chord describe bindings")
    (action . (("Call Interactively" . call-interactively)
               ("Add to kill-ring" . kill-new)))
    (candidates . (lambda ()
                    (mapcar (lambda (pair) (cdr pair))
                            key-chord-list)))
    (real-to-display . (lambda (real)
                         (format "%s - %s"
                                 (caar (remove-if-not
                                        (lambda (pair) (string= (cdr pair) real))
                                        key-chord-list))
                                 real)))))

beginning-of-defun でトップレベルの開き括弧まで移動させる

次のように関数定義内で行頭に開き括弧がある場合にその内側から beginning-of-defun で関数の先頭に移動しようとすると、 デフォルトの挙動では (let ...) の開き括弧の位置にカーソルが移動してしまいます。
行頭に開き括弧があったら、それを関数の開始位置として認識しているっぽいです。

(defun foo ()
(let (bar)
  ; ここで beginning-of-defun すると let の開き括弧に行く
)
)

そこからもう一度 beginning-of-defun すれば (defun ...) の開き括弧に行ってくれるんですが、なんか嫌です。
一発で関数の頭まで行ってほしい。

そこで、beginning-of-defun を実行した後にリストのネストの深さが 0 になるまで backward-up-list を繰り返すようにアドバイスを作りました。

(defun list-depth ()
  (nth 0 (parse-partial-sexp (point-min) (point))))

(defadvice beginning-of-defun
  (after beginning-of-defun-concerning-list-depth activate)
  (while (> (list-depth) 0)
    (backward-up-list)))
(ad-activate 'beginning-of-defun)

作ったんですが、describe-function で beginning-of-defun のヘルプを見たら

(setq defun-prompt-regexp nil)
(setq open-paren-in-column-0-is-defun-start nil)

だけでいいと分かりました。
なんか open-paren-in-column-0-is-defun-start っていう変数名がそのまんまですね。

save-excursionとカーソル周辺のブロック(関数など)の自動インデントを組み合わせてみた

  • Emacs Lisp を書いているときに自動インデントしたいときは C-M-a(beginning-of-defun)で関数の頭に移動して C-M-q(indent-pp-sexp)しているが、2ストロークなので面倒くさくなってきた
  • 1つにまとめよう
  • どうせだから save-excursion で囲んだらいいんじゃね?
  • どうせだから自分が良く使う他の言語でも同じフィーリングで使えるようにしたらいいんじゃね?

というわけで以下のようになりました。

言語によっては beginning-of-defun や indent-pp-sexp に相当するコマンド・関数が使えないこともあるので、その場合は

  • ルールで指定された関数を使ってブロックの先頭に移動した後で
  • ルールで指定された関数を使ってブロックの末尾に移動し、
  • その間をインデント

するようにしました。

なので、移動する関数を適当にいじるとカーソル位置などをもとに好きな範囲をインデントできます。

;; block-indent.el
;; author: sonota
;; license: GPL

;;;;
;; ルールの書式:
;;   ((modes) ; 関連付けるメジャーモードのリスト
;;    ブロック先頭に移動する関数
;;    ブロック末尾に移動する関数
;;    インデントを行う関数
;;    引数に開始・終了位置が必要なら non-nil)

;; .emacs で次のようにルールを追加する

;; (add-to-list
;;  'block-indent:rules
;;  '((ruby-mode)
;;    ruby-beginning-of-defun
;;    nil
;;    ruby-indent-exp nil))

;; (add-to-list
;;  'block-indent:rules
;;  '((js2-mode)
;;    beginning-of-defun
;;    forward-sexp
;;    indent-region t))

(defvar block-indent:default-rule
  '(nil
    backward-paragraph
    forward-paragraph
    indent-region t)
  "デフォルトのルール(ルールが見つからなかったときに使われる)")

(defvar
  block-indent:rules
  '(((emacs-lisp-mode lisp-interaction-mode)
     beginning-of-defun
     nil
     indent-pp-sexp nil))
  "ルールのリスト")

(defun block-indent:select-rule ()
  "メジャーモードをもとにルールを選択"
  (let (rule modes)
    (setq rule
          (car 
           (remove-if-not
            (lambda (x)
              (memq major-mode (nth 0 x)))
            block-indent:rules)))
    (unless rule
      (setq rule block-indent:default-rule))
    rule))

(defun block-indent:sub (rule)
  (let ((beginning-func (nth 1 rule))
        (forward-func   (nth 2 rule))
        (indent-func    (nth 3 rule))
        (region-args-p  (nth 4 rule))
        (beg nil))
    
    (funcall beginning-func)            ; ブロックの先頭に移動
    (setq beg (point))                  ; 先頭位置を保存
    (when forward-func
      (funcall forward-func))           ; ブロック末尾に移動
      
    ;; インデント
    (if region-args-p
        (funcall indent-func beg (point))
      (funcall indent-func))))

(defun block-indent ()
  (interactive)
  (save-excursion
    (block-indent:sub (block-indent:select-rule))))

(provide 'block-indent)

Emacs: *Apropos* バッファの見出しにタイプ別で色を付ける

なんだか Emacs Lisp の練習問題みたいなネタですね。

emacs-highlight-apropos

最後の行から上向きに 1行ずつ見ていき、

  • 見出し行でなければ "Function", "Variable" などのタイプを item-type にセット
  • 見出し行なら item-type をもとにフェイス設定

という動作をしています。

他のトピック: アドバイス連想配列、テキストプロパティ

;; hi-apro.el
;; author: sonota
;; license: GPL

(defvar hi-apro:type2face-alist
;;(setq hi-apro:type2face-alist
  '(("Command"  . font-lock-keyword-face)
    ("Face"     . font-lock-string-face)
    ("Function" . font-lock-function-name-face)
    ("Group"    . font-lock-constant-face)
    ("Macro"    . font-lock-warning-face)
    ("Plist"    . font-lock-type-face)
    ("Variable" . font-lock-variable-name-face)
    ("Widget"   . font-lock-builtin-face)
    ))

(defvar hi-apro:default-header-face
  'font-lock-comment-face)


(defun hi-apro:heading-line-p ()
  ;;(interactive)
  (eq 'bold (face-at-point)))

(defun hi-apro:type-of-item ()
  (save-excursion
    (goto-char (+ 2 (point)))          ; forward-char だと動かない
    (if (thing-at-point 'word)
        (substring-no-properties (thing-at-point 'word))
      nil)))

(defun hi-apro:type2face (type)
  (or (assoc-default type hi-apro:type2face-alist)
      hi-apro:default-header-face))

(defun hi-apro:sub ()
  (let ((item-type nil)
        (face nil))
    (goto-char (point-max))
    (while (> (point) (point-min))
      (unless (hi-apro:heading-line-p)
        (setq item-type (hi-apro:type-of-item)))
      (when (hi-apro:heading-line-p)
        (setq face (hi-apro:type2face item-type))
        (let ((beg (point)))
          (end-of-line)
          (put-text-property beg (point) 'face face)))
      ;;(message "%d" (point)) (sit-for 0.5)
      (forward-line -1)
      )))

(defun hi-apro ()
  ;;(interactive)
  (setq buffer-read-only nil)
  (save-excursion (hi-apro:sub))
  (setq buffer-read-only t))

(defadvice apropos
  (after hi-apro:advice activate)
  (hi-apro))
(ad-activate 'apropos)

;; (describe-function 'apropos)
;; (ad-remove-advice 'apropos 'after 'hi-apro:advice)