Author Archives: AKIYAMA

2026-03-14 ,

org-modeでエクスポート時にソースコードブロックを評価しないようにする

org-modeのソースコードブロック( #+BEGIN_SRC ~ #+END_SRC )は非常に便利で特に評価結果を生成してくれるところが素晴らしいですが、エクスポートするたびにソースコードを再評価するか聞かれるのは煩わしいところです。 もちろんエクスポートのたびに毎回評価した方が良いこともあるでしょうが、私の利用状況ではほとんどの場合エクスポート時の評価は必要ありません。

エクスポート時の評価を抑制する方法はいくつかありますが、最も普通なのはヘッダー引数 :eval を指定することではないでしょうか。 ヘッダー引数 :evalno-export または never-export を指定することでエクスポート時に評価しないようにできます。(参考: Evaluating Code Blocks)

例えば次のように :eval no-export を付けるとエクスポート時に評価されずに今 #+RESULTS: のところに生成されている結果がそのままエクスポートされます。

Emacs Lispで1+2+3を計算するには次のようにします。
#+name: example1
#+begin_src elisp :exports both :eval no-export
(+ 1 2 3)
#+end_src
結果:
#+RESULTS: example1
: 6

他にもキャッシュ機能を使えばソースコードの内容(ハッシュ値)が変わったときだけ評価するようにもできます。

Emacs Lispで1+2+3を計算するには次のようにします。
#+name: example1
#+begin_src elisp :exports both :cache yes
(+ 1 2 3)
#+end_src
結果:
#+RESULTS[8bb6d9b0bd19e634ef0ab976273b825967dfdc03]: example1
: 6

ただ、正直キャッシュまでは必要ない場合がほとんどです。大抵は書いている途中に手動で評価して確認し、それをそのままエクスポート時に出力してくれれば十分です。

他にもorg-confirm-babel-evaluateをnilにするという方法もありますが、これは確認せずに全て評価するという意味なのでセキュリティ的な懸念があります。

というわけで私は結果を出力するソースコードブロックにはいつも :eval no-export を付けていたのですが、インラインソースコードブロックを書くときにはウンザリしていました。例えば次のようにいちいち [:eval no-export] と書かなければなりません。

2750円から3000円へ値上がり(src_calc[:eval no-export]{100*(3000/2750-1)} {{{results(=9.090909091=)}}} %増)

打つのが面倒というのもありますが、見返したときにゴチャゴチャしていて読みづらいのが一番の問題です。

そもそもほとんどのソースコードブロックに :eval no-export を指定するのであれば、デフォルト値がそうなっていれば済む話ではないでしょうか。

調べてみると、次の書き方でorgファイル全体のデフォルトを設定できるようです。(参考: Using Header Arguments)

#+PROPERTY: header-args :eval no-export

これは良いものだと思いますが(こんなの前からあった?)、でもほとんどのファイルにこれを書くのも面倒です。

もう少し調べてみるとob-core.el内で定義されている変数org-babel-default-header-argsorg-babel-default-inline-header-argsを修正すればデフォルト値を変えられるようでした。

通常のソースコードブロックとインラインソースコードブロックとで変数が違います。

;; ob-core.elより
(defvar org-babel-default-header-args
  '((:session . "none") (:results . "replace") (:exports . "code")
    (:cache . "no") (:noweb . "no") (:hlines . "no") (:tangle . "no"))
  "Default arguments to use when evaluating a source block.
...略...")
(put 'org-babel-default-header-args 'safe-local-variable
     (org-babel-header-args-safe-fn org-babel-safe-header-args))

(defvar org-babel-default-inline-header-args
  '((:session . "none") (:results . "replace")
    (:exports . "results") (:hlines . "yes"))
  "Default arguments to use when evaluating an inline source block.")
(put 'org-babel-default-inline-header-args 'safe-local-variable
     (org-babel-header-args-safe-fn org-babel-safe-header-args))

(余談ですが、 :exports が通常では code で、インラインでは results になっているのが興味深いところです)

なのでこれを次のようにして書き替えてしまえば良さそうです(init.el等で)。

;; ソースブロックのヘッダー引数で :eval no-export をデフォルトにする。
(with-eval-after-load "ob-core"
  (push '(:eval . "no-export") org-babel-default-inline-header-args)
  (push '(:eval . "no-export") org-babel-default-header-args))
;; (setf (alist-get :eval org-babel-default-header-args) "no-export")でも良いのかもしれないけど……非破壊的なalist設定関数って無いんだよね。いや、破壊してもいいのかもしれないけど……copyしてsetする? マニュアルにはassq-delete-allしてconsする設定例が載っているなぁ

ちなみに変数に safe-local-variable プロパティの設定があるのでファイルローカル変数で指定できるのかなと思ってやってみたのですが (:eval . "no-export") の指定は安全だと見なされないようです。何が安全かは定数org-babel-safe-header-argsで決まっているので、そこを修正すれば通るようにはなります。どのみち指定しづらいですし、ファイル全体で指定したければ #+PROPERTY: header-args :eval no-export で設定した方が良いでしょう。

2750円から3000円へ値上がり(src_calc{100*(3000/2750-1)} {{{results(=9.090909091=)}}} %増)

# Local Variables:
# org-babel-default-inline-header-args: ((:eval . "never-export") (:session . "none") (:results . "replace") (:exports . "results") (:hlines . "yes"))
# End:

というわけでデフォルトを :eval no-export にしたのでインラインソースコードブロックが書きやすくなりました。

2750円から3000円へ値上がり(src_calc{100*(3000/2750-1)} {{{results(=9.090909091=)}}} %増)

……まぁ、比較的。それでも見づらさは残りますけどね。

ここからは余談ですが、式が出力されても良いならCalcのEmbedded Modeを利用する手もあります。例えば次のように書いて$と$の間で C-x * u と押すと結果が => の右に挿入されます。

2750円から3000円へ値上がり($100 (3000/2750 - 1) =>$ %増)

マクロを使えば => の右側だけ取り出すことも一応できます。

#+MACRO: calc-result (eval (string-trim $1 ".*=> " "\\$"))
2750円から3000円へ値上がり({{{calc-result($100 (3000/2750 - 1) => 9.090909091$)}}}%増)

区切りの $ は必要ですが、calc-embedded-open-formulaあたりで区切りを変更すれば無くせるかもしれません。

うーん、でもインラインソースコードブロックと大差ないような気もしますね。

ところで #+BEGIN_SRC~#+END_SRC のブロックを何と呼ぶかいつも困っています。ここでは「ソースコードブロック」と書いてみましたが、これまで私は「ソースブロック」と書くことが多かったような気がします。マニュアルでは「source code block」「code block」「source block」が混在しています。「src block」と書いてあるのもどこかで見たような気もします。

2026-02-22 ,

org-mode 9.8とインライン画像

org-modeの9.8がリリースされたようです。

Org mode 9.8 is out : r/orgmode

変更点:

Release notes | Org mode

早速更新しましたが、Emacsを再起動すると古いバージョンと混ざっているというような警告が出たので M-x package-recompile-all したら解消しました。

まだ詳細な変更点には目を通していないのですが、インライン画像まわりの構造が変わったようです。

従来「インライン画像表示」と呼ばれていたものが「リンクプレビュー」に変わりました。つまり、これまで画像へのfileリンク(またはattachmentリンク)を画像化するだけの機能だったものが、任意のリンクタイプにオーバーレイを適用する機能に一般化されました。

これに伴い私のインライン画像に対する拡張も修正しました。

misohena/org-inline-image-fix: A collection of fixes related to the image display feature in org-mode

edrawも通常fileリンクを編集するコマンドに若干影響があったので(バイトコンパイル時に警告が出ていたので)修正しました。

misohena/el-easydraw: Embedded drawing tool for Emacs

2026-02-15 ,

org-modeスプレッドシートへの数式入力を改善する

org-modeのテーブル(表、スプレッドシート)に数式を入力する方法として C-c = (org-table-eval-formula) や C-c ' (org-table-edit-formulas)があります。(参照: Editing and debugging formulas (The Org Manual))

C-c ' は別バッファに数式を表示してそれを編集できるようにします。数式入力用のバッファ内では、ポイントが当たっている(フィールド/カラム)参照やその参照先をハイライトする機能もあります。また、Shift+矢印キーで参照先を変更する機能もあります。

一方 C-c = はミニバッファから数式を入力するので手軽ですが、 C-c ' で使える便利な機能が使えません。しかしミニバッファといえどもEmacsのバッファではあるので C-c ' で開く数式編集バッファと同じ事ができない理由はありません。

また、前回少し言及したように、何も入力していない段階でShift+矢印キーを押したときに新しい参照を新規で追加できるようになっていれば C-c = S-<left> S-<left> * S-<left> というキー操作で $4=$2*$3 を入力するようなことができるはずです。これができれば座標(列番号)を数える必要性はグッと減ります。

というわけで書いたコード:

my-org-table.el

数式入力ミニバッファの改善

まずはorg-table-eval-formula(C-c =)でのミニバッファ入力中にorg-table-edit-formulas(C-c ')と同じキー操作ができるようにします。minibuffer-setup-hookorg-table-edit-formulasと同等の初期化処理をしてやれば良いでしょう。

;;;; 数式入力ミニバッファの改善

;; `org-table-eval-formula'(C-c =) のミニバッファ入力中に
;; `org-table-edit-formulas'(C-c ')と同じキー操作ができるようにする。

(defvar my-org-table-formula-minibuffer-map
  ;; `org-table-fedit-map'から使えそうなものだけを取りだしたキーマップ。
  (let ((km (make-sparse-keymap)))
    (define-key km "\C-c}" 'org-table-fedit-toggle-coordinates)
    (define-key km [(meta shift up)]    'org-table-fedit-line-up)
    (define-key km [(meta shift down)]  'org-table-fedit-line-down)
    (define-key km [(shift up)]    'org-table-fedit-ref-up)
    (define-key km [(shift down)]  'org-table-fedit-ref-down)
    (define-key km [(shift left)]  'org-table-fedit-ref-left)
    (define-key km [(shift right)] 'org-table-fedit-ref-right)
    (org-defkey km [(meta up)]     'org-table-fedit-scroll-down)
    (org-defkey km [(meta down)]   'org-table-fedit-scroll)

    ;; M-S-p等でも操作できるようにしてみる。
    (define-key km [(control meta ?p)] 'org-table-fedit-ref-up)
    (define-key km [(control meta ?n)] 'org-table-fedit-ref-down)
    (define-key km [(control meta ?b)] 'org-table-fedit-ref-left)
    (define-key km [(control meta ?f)] 'org-table-fedit-ref-right)
    km))

(defun my-org-table-eval-formula:around (old-fun &rest args)
  (let ((pos (point-marker)))
    (minibuffer-with-setup-hook
        ;; ミニバッファが開いたときに呼ばれる。
        (lambda ()
          ;; 元orgバッファの位置を入力バッファのローカル変数にする。
          (setq-local org-pos pos)
          ;; キー割り当てを追加する。
          (use-local-map (make-composed-keymap
                          my-org-table-formula-minibuffer-map
                          (current-local-map)))
          ;; 参照しているセルをハイライトする。
          (add-hook 'post-command-hook #'org-table-fedit-post-command t t))
      ;; 元の関数を呼び出す。
      (apply old-fun args))))
(advice-add #'org-table-eval-formula :around #'my-org-table-eval-formula:around)

数式入力時のS-矢印キーを改善

次にShift+矢印キーで参照先を移動する機能を改善します。

改善点:

  • @だけの場合 (@の後に数字等が無い場合も含む)
  • $だけの場合 ($の後に数字等が無い場合も含む)
  • 参照が無いところでの新規追加
  • @<@>>@I+1 への対応
  • $<$>> への対応
;;;; 数式入力時のS-矢印キーを改善

;; `org-table-fedit-shift-reference'(S-矢印キー)を改善する。
;; - @だけの場合 (@の後に数字等が無い場合も含む)
;; - $だけの場合 ($の後に数字等が無い場合も含む)
;; - 参照が無いところでの新規追加
;; - @<や@>、@I+1への対応
;; - $<や$>への対応
(defun my-org-table-fedit-shift-reference (dir)
  (cond
   ;; A E&-like reference (Same as $5)
   ((org-in-regexp "\\(\\<[a-zA-Z]\\)&")
    (if (memq dir '(left right))
        (org-table--rematch-and-replace 1 (eq dir 'left))
      (user-error "Cannot shift reference in this direction")))
   ;; A B3-like reference
   ((org-in-regexp "\\(\\<[a-zA-Z]\\{1,2\\}\\)\\([0-9]+\\)")
    (if (memq dir '(up down))
        (org-table--rematch-and-replace 2 (eq dir 'up))
      (org-table--rematch-and-replace 1 (eq dir 'left))))
   ;; Examples:
   ;;   @1 @+1 @-1 @0 @< @>> @I @+II @-III @I+2 @-II-4
   ;;   @1$3 @-1$+4
   ;;   @I..II
   ;;   @I..@II
   ;;   $1 $+1 $-1 $0 $< $>>
   ((org-in-regexp
     (concat
      ;; 1: Row spec
      "\\("
      "\\(?:@\\|\\.\\.\\)"
      ;;    @+I (?2: +1 )
      "\\(?:\\(?:[-+]?I+\\([-+][0-9]+\\)\\)"
      ;;    @ (?3: +I | +1 | < | > )
      "\\|\\([-+]?\\(?:I+\\>\\|[0-9]+\\)\\|<+\\|>+\\)\\)?"
      "\\)"
      ;; 4: Column spec after row spec
      ;;    $ (?5: +1 | < | > )
      "\\(\\$\\([-+]?[0-9]+\\|<+\\|>+\\)?\\)?"
      ;; 6: Column spec alone
      ;;    $ (?7: +1 | < | > )
      "\\|" "\\(\\$\\([-+]?[0-9]+\\|<+\\|>+\\)?\\)"))
    (cond
     ;; Up or Down
     ((memq dir '(up down))
      (cond
       ((match-beginning 2) ;; @+I+2
        (org-table--rematch-and-replace 2 (eq dir 'up) t))
       ((match-beginning 3) ;; @1
        (org-table--rematch-and-replace 3 (eq dir 'up) t))
       ((match-beginning 1) ;; @_
        (goto-char (1+ (match-beginning 1)))
        (insert (my-org-table-fedit-current-line-str dir)))
       (t
        (when (or (match-beginning 4) (match-beginning 6)) ;; _$
          (goto-char (or (match-beginning 4) (match-beginning 6))))
        (insert "@" (my-org-table-fedit-current-line-str dir)))))
     ;; Left or Right
     (t
      (cond
       ((match-beginning 5) ;; @ $1
        (org-table--rematch-and-replace 5 (eq dir 'left)))
       ((match-beginning 4) ;; @ $
        (goto-char (1+ (match-beginning 4)))
        (insert (my-org-table-fedit-current-column-str dir)))
       ((match-beginning 7) ;; $1
        (org-table--rematch-and-replace 7 (eq dir 'left)))
       ((match-beginning 6) ;; $_
        (goto-char (1+ (match-beginning 6)))
        (insert (my-org-table-fedit-current-column-str dir)))
       (t
        (when (match-end 1) ;; @_
          (goto-char (match-end 1)))
        (insert "$" (my-org-table-fedit-current-column-str dir)))))))
   ;; Add a new reference
   (t
    (if (memq dir '(up down))
        (insert "@" (my-org-table-fedit-current-line-str dir))
      (insert "$" (my-org-table-fedit-current-column-str dir))))))
(advice-add #'org-table-fedit-shift-reference :override
            #'my-org-table-fedit-shift-reference)

(defun my-org-table-fedit-current-column-str (&optional dir)
  ;; (pcase dir ('left "-1") ('right "+1") (_ "+0"))
  (number-to-string
   (+
    (with-current-buffer (marker-buffer org-pos) (org-table-current-column))
    (pcase dir ('left -1) ('right 1) (_ 0)))))

(defun my-org-table-fedit-current-line-str (&optional dir)
  (pcase dir ('up "-1") ('down "+1") (_ "+0"))
  ;; 行番号を絶対指定にしたい場合は↓を使う
  ;; (number-to-string
  ;;  (+
  ;;   (with-current-buffer (marker-buffer org-pos) (org-table-current-line))
  ;;   (pcase dir ('up -1) ('down 1) (_ 0))))
  )

;; `org-table-shift-refpart'を改善する。
;; - @>や$<<への対応
(defun my-org-table-shift-refpart:around (old-fun ref &optional decr hline)
  (if (and (stringp ref) (> (length ref) 0) (memq (aref ref 0) '(?< ?>)))
      (let* ((ch (aref ref 0))
             (delta (if (xor decr (eq ch ?>)) -1 1))
             (new-len (max 1 (+ (length ref) delta))))
        (make-string new-len ch))
    (funcall old-fun ref decr hline)))
(advice-add #'org-table-shift-refpart :around
            #'my-org-table-shift-refpart:around)

org-table-fedit-shift-referenceは丸丸置き換えてしまうことにしました。

ハイライトが@>や$<といった形式に対応していない問題が残っていますが、org-table-show-referenceは手を入れづらい構造なので諦めました。

参照先変更時の不要なスクロールを抑制する

Shift+矢印キーで参照先を変更したとき、表の先頭がウィンドウの先頭に来るようにスクロールされてしまいます。それが良い場合もあるのでしょうが、気に入らないので不要なスクロールはしないようにします。

;; `org-table-show-reference'を改善する。
;; - 不要なスクロールをしない
(defun my-org-table-show-reference:around (old-fun &rest args)
  (cl-letf* ((old-set-window-start (symbol-function 'set-window-start))
             ((symbol-function 'set-window-start)
              (lambda (window pos &rest args)
                (unless (pos-visible-in-window-p pos window)
                  (apply old-set-window-start window pos args)))))
    (apply old-fun args)))
(advice-add #'org-table-show-reference :around
            #'my-org-table-show-reference:around)

長い関数の最後あたり(org-table-show-referenceset-window-startを呼び出しているあたり)を修正しなければならないのでかなり無理矢理です。

完全に何もしないようにしてしまっても良いかもしれません。

結果

書いたコードはGistにmy-org-table.elというファイルで置いておきます。これまでにorg-table.elに対して行った他の全ての改善も一緒に入れておきました。

init.elで次のようにすれば修正が適用されると思います。

(with-eval-after-load "org"
  (require 'my-org-table))

本当はこういうのは本家にパッチでも送れば良いんでしょうけどやりとりが面倒ですしね~(みんなそうだからいつまで経っても大本が直らない説)