UP | HOME

Emacs の Prettify-symbols を複数文字対応にする

概要

[2022-02-24 Thu], . 作成。

srd-emacs-font-setting.png
図 1: 設定後のスクリーンショット

Emacs には、ある文字列を 1 文字に変換して表示する prettify-symbols-mode というものがある。

表示を変えるだけでファイルデータ自体は変わらないので、例えばラムダ式の lambdaλ にして可読性を向上させよう、という類の機能である。

一方、Emacs には composite.el という機能がデフォルトで組み込まれている。これは……正直もう一度触る気になれないのだが、「複数の文字を組み合わせて一つの文字として振る舞わせる」ことができる。

このコンポジット機能を使えば、を文字列を文字列で置換することが可能になる。この方法と、設定例を示してみる。

prettify-symbols とは

  ;; prettify for all text-modes
  (add-hook 'text-mode-hook
            (lambda ()
              (push '("alpha" . ?α) prettify-symbols-alist)
              (push '("omega" . ?ω) prettify-symbols-alist)
              ))

実際は他にも書き方はあると思うが、文字列とキャラクタの対応を prettify-symbols-alist に追加している。

自分には上のコードの lambdaλ に見えている。この文字の上で M-x describe-character すると、

               position: 891 of 1788 (50%), column: 13
              character: l (displayed as l) (codepoint 108, #o154, #x6c)
                charset: ascii (ASCII (ISO646 IRV))
  code point in charset: 0x6C
                 script: latin
                 syntax: w      which means: word
               category: .:Base, L:Left-to-right (strong), a:ASCII, l:Latin, r:Roman, u:Lowercase
               to input: type "C-x 8 RET 6c" or "C-x 8 RET LATIN SMALL LETTER L"
            buffer code: #x6C
              file code: #x6C (encoded by coding system utf-8-unix)
                display: composed to form "lambda" (see below)

  Composed with the following character(s) "ambda" by the rule:
    (?λ)
  The component character(s) are displayed by these fonts (glyph codes):
   λ: ftcrhb:-????-Sarasa Mono J-normal-normal-normal-*-14-*-*-*-*-0-iso10646-1 (#xB89A)
  See the variable ‘reference-point-alist’ for the meaning of the rule.

以上のようになる。

composite.el の書き方

さて、問題の composite.el である。詳細は Emacs 上で M-x describe-variable reference-point-alist をすると見ることができる。

  Documentation:
  Alist of symbols vs integer codes of glyph reference points.
  A glyph reference point symbol is to be used to specify a composition
  rule in COMPONENTS argument to such functions as ‘compose-region’.

  The meaning of glyph reference point codes is as follows:

      0----1----2 <---- ascent       0:tl or top-left
      |         |                       1:tc or top-center
      |         |                       2:tr or top-right
      |         |                       3:Bl or base-left     9:cl or center-left
      9   10   11 <---- center       4:Bc or base-center  10:cc or center-center
      |         |                       5:Br or base-right   11:cr or center-right
    --3----4----5-- <-- baseline     6:bl or bottom-left
      |         |                       7:bc or bottom-center
      6----7----8 <---- descent      8:br or bottom-right

  Glyph reference point symbols are to be used to specify a composition
  rule of the form (GLOBAL-REF-POINT . NEW-REF-POINT), where
  GLOBAL-REF-POINT is a reference point in the overall glyphs already
  composed, and NEW-REF-POINT is a reference point in the new glyph to
  be added.

  For instance, if GLOBAL-REF-POINT is ‘br’ (bottom-right) and
  NEW-REF-POINT is ‘tc’ (top-center), the overall glyph is updated as
  follows (the point ‘*’ corresponds to both reference points):

      +-------+--+ <--- new ascent
      |       |  |
      | global|  |
      | glyph |  |
   -- |       |  |-- <--- baseline (doesn’t change)
      +----+--*--+
      |    | new |
      |    |glyph|
      +----+-----+ <--- new descent

  A composition rule may have the form (GLOBAL-REF-POINT
  NEW-REF-POINT XOFF YOFF), where XOFF and YOFF specify how much
  to shift NEW-REF-POINT from GLOBAL-REF-POINT.  In this case, XOFF
  and YOFF are integers in the range -100..100 representing the
  shifting percentage against the font size.

最初の図が出てきた時点でやる気はゼロだと思うので、まずは例を見せよう。

  ;; prettify for all text-modes
  (add-hook 'text-mode-hook
            (lambda ()
              (push '("tex" 32 (Br Bl -70 0) 119827 (Br cl -70 30) 119812 (Br Bl -50 0) 119831 (Br Bl -70 0) 32) prettify-symbols-alist)
              ;; Le bon Dieu est dans le détail
              ;; https://practicaltypography.com/line-spacing.html
              (setq line-spacing 1)
              ))

これを 最初のソースコード と比べると、以下が文字を組んだ結果となる。

32 (Br Bl -70 0) 119827 (Br cl -70 30) 119812 (Br Bl -50 0) 119831 (Br Bl -70 0) 32
要素 説明
32 スペース。
(Br Bl -70 0) 前の文字(スペース)と次の文字をくっつけて、次の文字を右に -0.7 文字分ずらす
119827 Mathematical Bold Capital T。
(Br cl -70 30) 前の文字の図中 5 番の位置と、次の文字の図中 9 番の位置をくっつけ、次の文字を右に -0.7 文字、上に 0.3 文字分ずらす
119812 Mathematical Bold Capital E。
(Br Bl -50 0) 前の文字の図中 5 番の位置と、次の文字の図中 3 番の位置をくっつけ、次の文字を右に -0.5 文字分ずらす
119831 Mathematical Bold Capital X。
(Br Bl -70 0) 前の文字の図中 5 番の位置と、次の文字の図中 3 番の位置をくっつけ、次の文字を右に -0.7 文字分ずらす
32 スペース。 0x20

この括弧で囲まれた要素が、いわば「糊」となって前後の文字を繋いでいる。上の例ではオフセット(ずらし)が必要になったが、使わない場合は単に (tl . bc) などとすればよい。

ちなみに十進数の数字は、例えば http://unicode-table.com/en/1d413 の HTML Code の数字を見れ分かる。

折角なので \( \LaTeX \) も組んだので持って帰ってください。

("latex" 32 (Br Bl -75 0) 119819 (cr bl -90 -55) 119808 (Br Bl -65 0) 119827 (Br cl -70 30) 119812 (Br Bl -50 0) 119831 (Br Bl -75 0) 32)

文字組み関数を定義

さて、余計な知識を付けたところで、本題に入ろう。やりたいことは単に「前後の文字をくっつける」だけなので、糊は (Br . Bl) だけで良い。あとは受け取ったリストを次から次へとくっつけていく。

  (defun private/prettify-list (l &optional merge)
    "Takes two lists and interleaves the (optional) second between each element of the first.  Used to
    create multi-character sequences for use with prettify-symbols mode.  If not supplied, MERGE defaults
    to '(Br . Bl)"
    (let ((merge (or merge '(Br . Bl)))
          (head (car l))
          (tail (cdr l)))
      (cond
       ((not (consp l))    '())
       ((not (consp tail))  (list head))
       (t (cons head
                (cons merge
                      (private/prettify-list tail merge)))))))

  (defun private/prettify-string (s &optional merge)
    "Takes a string and an optional list, and returns a list of the string's characters with MERGE
    interleaved between each character, for use with prettify-symbols mode.  If no MERGE is supplied,
    uses the private/prettify-list default."
    (private/prettify-list (append s nil) merge))

任意の設定ファイルに上の関数を登録してから以下のようにする。 org-mode での例を示す。

  (add-hook 'org-mode-hook
            (lambda ()
              (push (cons "#+BEGIN_SRC"     (private/prettify-string "╱╱  CODE:  ╱╱")) prettify-symbols-alist)
              (push (cons "#+END_SRC"       (private/prettify-string "╱╱  :CODE  ╱╱")) prettify-symbols-alist)
              ;; ...
              (push (cons "#+BEGIN_VERSE"   (private/prettify-string "“— v e r s e")) prettify-symbols-alist)
              (push (cons "#+END_VERSE"     (private/prettify-string "ə s ɹ ə ʌ —”")) prettify-symbols-alist)
              (prettify-symbols-mode)
              ))

Created: 2022-02-27 Sun 01:33