Last update: 2025-01-23

Emacs lisp で dedent を実装する   Emacs EmacsLisp

Pythonには textwrap.dedent という関数があります。 しかし、Emacs Lisp にはそのような関数は標準では用意されていません。そこで、以下のような関数を作成してみました。

コード

(defun my/dedent-string (text)
  "Remove common leading whitespace from each line in TEXT."
  (let* ((lines (split-string text "\n")) ; 文字列を行ごとに分割
         (non-empty-lines (seq-filter (lambda (line) (not (string-match-p "^[ \t]*$" line))) lines))
         (common-indent (if non-empty-lines
                            (apply 'min
                                   (mapcar (lambda (line)
                                             (if (string-match "^\\([ \t]*\\)" line)
                                                 (length (match-string 1 line))
                                               0))
                                           non-empty-lines))
                          0)))
    (mapconcat (lambda (line)
                 (if (string-match (format "^ \\{%d\\}" common-indent) line)
                     (replace-match "" nil nil line)
                   line))
               lines
               "\n")))

説明

  1. 文字列を分割: split-string を使って、改行ごとに文字列を分割します。
  2. 空行を除外: seq-filter で空行(空白やタブのみの行)を除外します。
  3. 共通のインデントを計算: 各行の先頭の空白の長さを計算し、その最小値を取得します。
  4. インデントを削除: 計算した共通のインデントを各行から削除します。
  5. 行を結合: mapconcat を使って再度行を結合します。

この関数は、与えられた複数行の文字列の中で、共通するインデントのみを削除します。

実行例

サンプルのtest-stringを渡した場合、以下のように出力されます。

test-string

line 1
    line 2
  line 3

my-indent を実行。

(my/dedent-string test-string)
line 1
    line 2
  line 3