2022-01-16 ,

org-modeでテーブルの列幅を指定してshrinkすると日本語で表示が乱れる件

(2022-01-16追記: 切り取る範囲の計算を修正しました。invisibleな文字があるので複数の答えがあり得て、その中で最も長いものを選ばなければなりませんでした)

phscrollのついでと言っては何ですがこれも以前から気になっていたので。

2022-01-16-fix-org-table-shrink-field.png

見るからに指定した幅以上の最小の幅で切り取っているのが原因ですね。私がカラム幅の指定をあまり使ってこなかったのはこういうのがあるからだったりします。超えない最大の幅で切り取って足りない分を埋めるのが良いでしょう。

問題は org-table–shrink-field 内にあるのでこれにadviceを追加することにしました。

(defun my-org-table--shrink-field (old-fun width align start end contents)
  (if (or (= start end)
          (org-table--shrunk-field)
          (= 0 width)
          (eq contents 'hline)
          (equal contents ""))
      ;; 関係ないところは従来のコードを呼び出す。
      (funcall old-fun width align start end contents)

    (let* ((lead (org-with-point-at start (skip-chars-forward " ")))
           (trail (org-with-point-at end (abs (skip-chars-backward " "))))
           (contents-width (org-string-width
                            (buffer-substring (+ start lead) (- end trail)))))
      (cond
       ((<= width contents-width)
        (let ((pre
               (and (> lead 0)
                    (org-table--make-shrinking-overlay
                     start (+ start lead) "" contents t)))
              (post
               ;; widthを超えない最後の位置を分割点とする。

               ;; Find cut location so that WIDTH characters are
               ;; visible using dichotomy.
               (let* ((begin (+ start lead))
                      (lower begin)
                      (upper (1+ (1- end))) ;;+
                      ;; Compensate the absence of leading space,
                      ;; thus preserving alignment.
                      (width (if (= lead 0) (1+ width) width))
                      (divpos
                       (progn
                         (while (> upper lower)
                           (let ((mean (ash (+ lower upper) -1)))
                             (if (< width (org-string-width (buffer-substring begin mean)))
                                 (setq upper mean)
                               (setq lower (1+ mean)))))
                         (1- upper)))
                      ;; 分割点までの幅を計算する。
                      (str-w (org-string-width (buffer-substring begin divpos))))
                 (org-table--make-shrinking-overlay
                  divpos
                  end
                  ;; 足りない分を空白で埋める。
                  (make-string (- width str-w) ? )
                  contents))))
          (if pre (list pre post) (list post))))
       (t
        ;; 関係ないところは従来のコードを呼び出す。
        (funcall old-fun width align start end contents))))))

(advice-add #'org-table--shrink-field
            :around #'my-org-table--shrink-field)

;; (advice-remove #'org-table--shrink-field
;;                #'my-org-table--shrink-field)

丸丸差し替えても良いのですが、結構長い関数だったので影響のある部分だけ別の処理をしてそれ以外は従来のコードを呼び出すようにしてみました。

幅広文字を考慮しているようでしていないというなんともよく分からないコードですね。

https://git.savannah.gnu.org/cgit/emacs/org-mode.git/tree/lisp/org-table.el?h=release_9.5.2#n3977