『TOPPOINT』を使った要約トレーニング法

学生時代から文章を書くことに苦手意識を持っていた。 小学校の読書感想文から大学院の修士論文まで、すべての文章執筆で苦労をした記憶がある。

社会人になってからもその苦手意識は続いていたのだが、コロナ禍で情報伝達がテキスト中心になったこと、マネージャーになって文章を書く機会が増えたことから、文章力を鍛えることにした。

そうは決心したものの、何から手をつけたらいいかわからない。「文章力をつけるには要約がよい」という意見を見聞きするので要約をやってみようと思う。しかし、要約をするための文章が見つからない。 目についた新聞記事や本を要約してはみるが、1つの文量がまちまちだったり、自分の要約が的を得ているのか判断がつかなかったりと、習慣には繋がらなかった。

そんなとき、たまたま『TOPPOINT』の紹介を受けた。 これを教材にしてみたところ、文章力のトレーニングに寄与した実感があったため、私なりの使い方を記す。

『TOPPOINT』とは

株式会社パーソナルブレーン社が発行している、ビジネス書を要約した月刊誌。 10冊分の書籍を各4ページに要約したコンテンツを毎月家に届けてくれる。

購読料は1年契約の場合、毎月1,210円(2024/09現在)

新刊ビジネス書の要約『TOPPOINT(トップポイント)』www.toppoint.jp

トレーニング方法

1回のトレーニングは、

  • ①本誌から興味のある1冊を選ぶ
  • ②本誌2-4ページ(約5,000字)を箇条書きで800字に要約
  • ③自分の要約と本誌1ページ目(in brief)を比較

の手順で行う。

この方法のメリットは

  • ビジネス現場で多用される箇条書きの形式でまとめる力がつくこと(in briefも箇条書き形式)
  • 自分の要約と本誌を比較することで、編集者の要約からの学びを得ることができること
  • 1回1時間程度の短時間で終わること

である。

この方法が成り立つのは、『TOPPOINT』が「原著 → 全3ページの要約(P. 2-4) → 全1ページの要約(P. 1) → リード文」というような、要約の多重構成となっているためである。 さらに学びを深めるのであれば140文字程度に要約して、目次ページのリード文と比較するというような使い方もできる。

要約の具体的方法

本誌に赤ペンで書き込みをし、要約文はScrapboxに書き溜めている。

2020年8月号掲載の『外資系コンサルが教えるプロジェクトマネジメント』を例に成果物を紹介する。

ドラフト

本誌へは赤ペンで要点を洗い出してアンダーラインを引いたり、①②のように印をつけている。

誌面で要点を洗い出す際には、構造や重要度など、筆者や編集者からのメッセージを読み取っていく。

例えば、何かを列挙する際の「まず」「つぎに」「そして」という接続表現、重要な点を記す際の「xxである。つまり...」「では、xxxはどうなるのか?それは...」というような言い換えや修辞的な質問文などだ。 これらの表現に敏感になると、要約がかなり楽になる。

アウトプット

Scrapboxにはアンダーラインを引いた部分を中心に転記する。

in briefで参考になることがあればアイコン付きでコメントをしている。 また、要約後の後作業としてリンクをつけておくことで、要約文章自体も情報資産化する。

文の濃度

要約には、正解があるものではないが、抜き出すポイントの違い、要約文の違い、など、自分と編集者の観点の違いは学びになる。

例えば、2020年10月号掲載の『よきリーダーは哲学に学ぶ』の中の一文。

私の要約文では

ブッダは苦しみは恐れや欲深さから生まれ、苦しみをなくすにはその根元を突き止めて、断ち切らなければならないと解いた。 ブッダの教えは組織戦略の思い込みを知るヒントがある。つまり、自分たちが他から切り離された存在だと考えると「自分対世界」の構図に基づき強迫観念が生じるが、他と依存し合う関係にあると捉えれば、その構図は崩れる。 先に見えない時代において、共感は極めて重要な戦略スキルとなる。

と本文を引用したが in brief では

ブッダの洞察をビジネス戦略に応用すると、市場の独占ではなく、協力を通した価値創造に焦点を置いたアプローチになる。その遂行にあたって重要になるのは、共感のスキルだ。

とシャープで濃度の高い文になっている。

「苦しみは恐れや...」を「洞察」と置き換え、「ブッダの教えは組織戦略の...」を「価値創造に焦点を置いたアプローチ」と置き換えるなど、情報密度の高さに学ぶことが多い。

まとめ

TOPPOINTを使った要約練習の方法を紹介した。

毎月全ての記事に対する要約をしているわけではなく、毎月気になる2, 3冊を選んでいた。 それでも数ヶ月経つ頃には、簡潔な文を書けるようになったり、構成を構造化できたりと、文章力の向上が実感できた。

つまみ食いをしてもスキル向上ができたのは「ビジネス書の質の高い要約を10冊分届けてくれる」という特性があったからで、新聞の社説や書籍では継続は難しかったと思う。 また、毎月ポストに投函されるというのも個人的には大事で、これがオンラインコンテンツであったなら、1日で忘れ去っていたはずだ。

今ではこのトレーニングを継続してはいないものの、当時作ったメモはいまでも参照することがあり、成果物自体も資産になっている。 特にビジネスの現場で文章力に悩む方に、この方法をおすすめしたい。

【解決済み】Emacs 30で日本語のデフォルトフォントが変わる

【2024/09/22追記】 本事象はemacs-30ブランチで解決済みです。 https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-09/msg01021.html


Emacs 30.0.91 のプレテスト版をビルドしてみたところ、日本語周りに挙動の変化があったので調査をしている。

この記事ではフォントの調査結果を載せているが、IME(macOS の日本語入力)の確定がワンテンポ遅れる挙動もあるような気もしている。 (ただし確定がワンテンポ遅れる挙動は spacemacs の設定を読み込むと再現しない。適切なフォント設定ができていると、顕在化しないのかもしれない)

環境

  • 対象バージョン: Emacs 30.0.91(NS版)
  • 起動方法: emacs -Q
  • M-x version:
    • GNU Emacs 30.0.91 (build 1, x86_64-apple-darwin23.1.0, NS appkit-2487.20 Version 14.1 (Build 23B74)) of 2024-09-14

事象

  • ひらがな・カタカナはヒラギノ
  • 漢字は Arial Unicode MS

で表示される。

二分探索でコミットを探って行ったところ、Android portの導入コミットで挙動が変わった模様。 ただしAndroid portの差分が大きいので、具体的な差分までは調査できていない。

Introduce an Android window system port for GNU Emacs · emacs-mirror/emacs@c71a520 · GitHub

フォントの確認方法

  • 文字の上で M-x describe-char
  • (face-font 'default nil ?開)(face-font 'default nil ?あ) を評価

改善方法

フォント設定を追加する。

(set-fontset-font nil 'japanese-jisx0208
                  (font-spec :family "Hiragino Sans"))

一応init.elにフォント設定を追加すれば直るけど、Emacsのデフォルトで適切なフォントを設定して欲しい気もする。

作業ログ

二分探索のコミットログ調査のメモなど

Emacs 30.0.31で日本語の変換候補の表示が遅延する - kuranari

iOSで外部キーボードを使う際の不満点

前の記事でiPhone向けの外部キーボードを購入したことを書いた。

Elecom TK-CM10BMKを買った - Kuranari::Log

使ってみると、macOSとは異なる不自由な点に気づいたので記してみる。

行頭/行末の移動

Ctrl-a / e での行頭/行末への移動が効いたり、効かなかったりする。
どうも、1行のインプット要素だと移動が可能で、複数行のインプット要素だと移動が出来ないようだ。home / end キーも同様に動作しないようで、プチストレスになっている。

Obsidianなど、アプリ側でキーバインドを定義してると思われるものでは、正しく移動できる。

Ctrl-mの挙動

IMEで日本語入力をしている際にCtrl-mで候補を確定しようとすると、確定 + 改行の動作となってしまう。EnterやCtrl-jでの確定であればこのようなことはおきない。

macOSではCtrl-mで確定をすることが多いので、無駄な改行を入れてしまい、BSで消すようなことをしている。

IMEのカスタマイズ性の低さ

macOSではIMEがONの場合のみ、セミコロン(;)を長音(ー)の入力に使用している。

macOSであれば比較的容易にこの設定ができるのだが、iOSではこのような設定項目が見つからず、やや不自由な状態になっている。

不満点は書いたものの、、

無設定でctrl n/p/b/fの移動、Ctrl d/h/kの削除、Ctrl i/j/mの制御コード入力ができるのはさすがだと思った。

一定程度慣れれば使える、というレベルに感じるので、もう少し使っていってみようと思う。

Elecom TK-CM10BMKを買った

 

 

iPhoneに接続する外付けキーボードが欲しくなり購入。こだわりは特になく、コンパクトなBluetoothキーボードの中で1番ピンときたものを選択した。

事前調査なしで購入したものの、意外とよかったのでそのレビュー

よかった点

Ctrl / CapsLockの切り替えスイッチ

CtrlはAの横にあって欲しいのでこの物理スイッチがあるのが嬉しい。iOS側でも制御はできるもののハードウエアで制御できるにこしたことはない

電源On/Offボタン

Bluetoothの接続が確立していると、ソフトウエアキーボードが表示されない状態になるため、物理的に接続解除ができることがありがたい。

電池付属

いわゆる battery included.

電池が付属されていて、箱を開けたらすぐ使えるというのは体験がとてもいい。エネループの在庫を切らしていたこともあって、電池を用意しなきゃな、と思いながら箱を開けてたら、まさかの電池内蔵でとても助かった。

まとめ

タブレット向けのよくあるキーボードという感じだが、細かいところに気が利いている製品だと感じた。

通常の19mmに対して、17mmとややコンパクトな作りだが、スマホ向けに使うのであれば十分。また、標準的なJIS配列なのも好みだ。

アルファベット部分は通常サイズで、右側の記号部分は小さい設計のものや、US配列を物理キーをベースにJIS配列にしているものではない、というのが暗黙的に求めていた条件だったのかもしれない。

MacBookを引っ張り出すほどではないけれど、フリック入力だとつらいという状況で、このキーボードを使って行ってみようと思う。

睡眠負債の解消

2024年のゴールデンウィークは適正な睡眠時間の把握、睡眠負債の解消を目的に、睡眠に注力して過ごした。

きっかけは、睡眠に関する情報をテレビで目にする機会が増えたこと。番組の中では、適正な睡眠時間を知る方法として、一定期間寝たいだけ寝る方法が紹介されていた。

その方法は下記の記事でも言及がされている。

これに関して興味深い実験があります。それは、「どれだけ眠れば寝不足は解消できるのか」を検証するために、健康な10人を14時間、毎日ベッドに入れ続けた調査です。

https://toyokeizai.net/articles/-/164998?page=3

実験

  • 4/28 - 5/5の7日間、寝たいだけ寝る
  • Fitbit charge 5で計測をする

寝たいだけ寝るのだが、そうは言っても14時間布団に入っているのは現実的ではないので12時間とした。21時に入り9時には起きるイメージ。

結果

f:id:kuranari_tm:20240526185737p:image

毎日の睡眠時間は10-11時間程度。

Fitbitのダッシュボードは8.5時間になっているが、夜間の覚醒時間の1.5-2時間は睡眠時間に含まれない。

睡眠負債の解消には3週間程度かかるという論文の報告通り、必要睡眠量はまだ多めなのだろう。予想としては、8.0-8.5時間が適正な睡眠時間なのではないかと感じる。

別の指標としては、安静時心拍数が下がった。

f:id:kuranari_tm:20240526110457j:image

ただし、睡眠中は心拍数が下がるから、単純に睡眠量が増えたから平均が引き下げられただけという可能性がある。さらにゴールデンウイーク中は階段の上り下りの回数が減ったこと、仕事中は心拍数が上がりがちだが全日休みだった、などの事情から睡眠との因果関係は不明である。

日中の感覚としては、思考がとてもクリアになった。これまで脳にモヤがかかっていた状態だったのに、それに気づけていなかったのは危険な状態だったようにも思う。寝る前にビール350mlを飲んでも次の日にはスッキリ目覚められた。

まとめ

1週間まとまった睡眠をとったことで、日頃いかに必要な睡眠を取れていないか自覚することができた。

睡眠量を減らして仕事をするよりも、きっぱり寝た方がよい意思決定ができるということも感じられた。睡眠時間の確保に重点を置いて生活をしていこうと思う。

 

参考文献

 

Emacs org-modeへの初コントリビュート体験記

背景

2023/11にEmacsのorg-modeにコントリビュートを行った。 OSSに対してGitHubのPull Requestでコントリビュートしたことは何度かあるが、Emacs / org-modeに対するコントリビュートは初めてで、さらにいうとメーリングリストでパッチを送るということ自体が初めてだった。

パッチの概要、時系列のタイムライン、この経験での学びを記載する。

パッチの概要

org-special-ctrl-a/etになっているという前提。 C-u 2 C-a のように前置引数が正の整数で org-beginning-of-line を実行すると、本当は見出し記号の直後にカーソルが移動することを期待するが、実際には行頭に移動してしまう。なお、C-u 0 C-aのようなファイル先頭方向に移動する際には期待通りの挙動を示す。

この挙動はmarkdown-modeに対して org-beginning-of-line と同等の機能を実装していくなかでの自動テストで発見した。 前置引数ありで org-beginning-of-line を呼び出し、それが運よく(運悪く?)見出しやリスト行に当たるということは少ないだろうから、markdown-mode向けの再実装をしなければ見つけられなかったと思う。

実際のパッチ

From 739c6636cd9e015ed214a6ccaed20cf75301a8d5 Mon Sep 17 00:00:00 2001
From: Tomohisa Kuranari <tomohisa.kuranari@gmail.com>
Date: Fri, 22 Sep 2023 22:38:26 +0900
Subject: [PATCH] org-beginning/end-of-line: Fix when moving to different line

* lisp/org.el (org-beginning-of-line, org-end-of-line): Fix issue with `org-special-ctrl-a/e' not working correctly when moving to different line
* testing/lisp/test-org.el (test-org/beginning-of-line, test-org/end-of-line): Add new tests.
---
 lisp/org.el              |  6 ++--
 testing/lisp/test-org.el | 62 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index d0b2355ea..7cd313c30 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -20331,7 +20331,7 @@ With argument N not nil or 1, move forward N - 1 lines first."
    (if (eq special 'reversed)
        (when (and (= origin bol) (eq last-command this-command))
          (goto-char refpos))
-    (when (or (> origin refpos) (= origin bol))
+     (when (or (> origin refpos) (<= origin bol))
        (goto-char refpos)))))
      ((and (looking-at org-list-full-item-re)
       (org-element-type-p
@@ -20347,7 +20347,7 @@ With argument N not nil or 1, move forward N - 1 lines first."
    (if (eq special 'reversed)
        (when (and (= (point) origin) (eq last-command this-command))
          (goto-char after-bullet))
-    (when (or (> origin after-bullet) (= (point) origin))
+     (when (or (> origin after-bullet) (>= (point) origin))
        (goto-char after-bullet)))))
      ;; No special context.  Point is already at beginning of line.
      (t nil))))
@@ -20402,7 +20402,7 @@ With argument N not nil or 1, move forward N - 1 lines first."
           (goto-char tags)
         (end-of-line)))
          (t
-         (if (or (< origin tags) (= origin (line-end-position)))
+          (if (or (< origin tags) (>= origin (line-end-position)))
           (goto-char tags)
         (end-of-line))))))
      ((bound-and-true-p visual-line-mode)
diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el
index 8355e2d77..63fd16b40 100644
--- a/testing/lisp/test-org.el
+++ b/testing/lisp/test-org.el
@@ -4460,6 +4460,16 @@ asd
      (let ((org-special-ctrl-a/e '(nil . nil)))
        (org-beginning-of-line)
        (looking-at "Headline"))))
+  (should
+   (org-test-with-temp-text "* TODO [#A] Headline\n<point>"
+     (let ((org-special-ctrl-a/e t))
+       (org-beginning-of-line 0)
+       (looking-at-p "Headline"))))
+  (should
+   (org-test-with-temp-text "<point>\n* TODO [#A] Headline"
+     (let ((org-special-ctrl-a/e t))
+       (org-beginning-of-line 2)
+       (looking-at-p "Headline"))))
   ;; At an headline with reversed movement, first move to beginning of
   ;; line, then to the beginning of title.
   (should
@@ -4480,6 +4490,18 @@ asd
       (this-command last-command))
        (and (progn (org-beginning-of-line) (bolp))
        (progn (org-beginning-of-line) (looking-at-p "Headline"))))))
+  (should
+   (org-test-with-temp-text "* TODO Headline\n<point>"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-beginning-of-line 0) (bolp))
+       (progn (org-beginning-of-line) (looking-at-p "Headline"))))))
+  (should
+   (org-test-with-temp-text "<point>\n* TODO Headline"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-beginning-of-line 2) (bolp))
+       (progn (org-beginning-of-line) (looking-at-p "Headline"))))))
   ;; At an item with special movement, first move after to beginning
   ;; of title, then to the beginning of line, rinse, repeat.
   (should
@@ -4488,6 +4510,14 @@ asd
        (and (progn (org-beginning-of-line) (looking-at-p "Item"))
        (progn (org-beginning-of-line) (bolp))
        (progn (org-beginning-of-line) (looking-at-p "Item"))))))
+  (should
+   (org-test-with-temp-text "- [ ] Item\n<point>"
+     (let ((org-special-ctrl-a/e t))
+       (org-beginning-of-line 0) (looking-at-p "Item"))))
+  (should
+   (org-test-with-temp-text "<point>\n- [ ] Item"
+     (let ((org-special-ctrl-a/e t))
+       (org-beginning-of-line 2) (looking-at-p "Item"))))
   ;; At an item with reversed movement, first move to beginning of
   ;; line, then to the beginning of title.
   (should
@@ -4496,6 +4526,18 @@ asd
       (this-command last-command))
        (and (progn (org-beginning-of-line) (bolp))
        (progn (org-beginning-of-line) (looking-at-p "Item"))))))
+  (should
+   (org-test-with-temp-text "- [X] Item\n<point>"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-beginning-of-line 0) (bolp))
+       (progn (org-beginning-of-line) (looking-at-p "Item"))))))
+  (should
+   (org-test-with-temp-text "<point>\n- [X] Item"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-beginning-of-line 2) (bolp))
+       (progn (org-beginning-of-line) (looking-at-p "Item"))))))
   ;; Leave point before invisible characters at column 0.
   (should
    (org-test-with-temp-text "[[https://orgmode.org]]<point>"
@@ -4598,6 +4640,14 @@ asd
        (and (progn (org-end-of-line) (looking-at-p " :tag:"))
        (progn (org-end-of-line) (eolp))
        (progn (org-end-of-line) (looking-at-p " :tag:"))))))
+  (should
+   (org-test-with-temp-text "* Headline1 :tag:\n<point>"
+     (let ((org-special-ctrl-a/e t))
+       (org-end-of-line 0) (looking-at-p " :tag:"))))
+  (should
+   (org-test-with-temp-text "<point>\n* Headline1 :tag:\n"
+     (let ((org-special-ctrl-a/e t))
+       (org-end-of-line 2) (looking-at-p " :tag:"))))
   (should
    (org-test-with-temp-text "* Headline2a :tag:\n** Sub"
      (org-overview)
@@ -4625,6 +4675,18 @@ asd
       (this-command last-command))
        (and (progn (org-end-of-line) (eolp))
        (progn (org-end-of-line) (looking-at-p " :tag:"))))))
+  (should
+   (org-test-with-temp-text "* Headline3 :tag:\n<point>"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-end-of-line 0) (eolp))
+       (progn (org-end-of-line) (looking-at-p " :tag:"))))))
+  (should
+   (org-test-with-temp-text "<point>\n* Headline3 :tag:\n"
+     (let ((org-special-ctrl-a/e 'reversed)
+      (this-command last-command))
+       (and (progn (org-end-of-line 2) (eolp))
+       (progn (org-end-of-line) (looking-at-p " :tag:"))))))
   (should
    (org-test-with-temp-text "* Headline2a :tag:\n** Sub"
      (org-overview)
-- 
2.42.0

https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/?id=93ebd64de

タイムライン

  • 2023/08/05
    • Copyright Assignmentのリクエスト
  • 2023/08/15
    • AssignmentのPDFを受信(from kuranari)
  • 2023/09/02
    • PDFにサインをしてEmailで返送。2023年時点では書類の郵送の必要はなく、メールで完結した。
    • (印刷、スキャンが手間でタスクを積みっぱなしにしていたら、事務局からリマインドをいただいてしまった
  • 2023/09/20
    • Assignmentのプロセスが完了の連絡受信
  • 2023/09/26
  • 2023/10/05
    • yantar92さんからレビューをもらう
    • line-number-at-posは使わないほうがいいのじゃないかという指摘。納得すぎる
  • 2023/10/08
    • レビューでの指摘を対応し再提出
  • 2023/11/08

不安な日々

メールを送る機会が少なくなっていて、メールの送信者を日本語の本名で送ってしまった。 またGmailでパッチファイルを添付したのだが、他の人が読めるファイル形式になっているのか、返信をもらうまで不安を抱えていた。

この辺りは、送信者名はAscii文字列でなければないだろうし、何か失敗があってもコントリビュートは恥をかきながら覚えようということで開き直ったりもしていた。

レビュワーへの感謝

レビュワーのyantar92さんからは、コミットメッセージの書き方、コードフォーマットの作法、効率的なアルゴリズムなどを丁寧に指摘していただいた。パッチに対してレビュワー自身がコードを改変したほうが労力は少なかっただろうに、一見さんの私に対して丁寧な教育をしてもらえたのがとても嬉しかった。業務、OSSを問わず、レビューをする際にはこの経験を生かしたいと思う。

まとめ

org-modeへの初のコントリビュートについてまとめた。 メーリスへの投稿から数週間レスがない時期などは、もしかして忘れられてないだろうかと不安になることもあったが、最終的にマージされた時の嬉しさは格別だった。

マージについてお礼を言った際に返ってきた

Music to our ears - thanks to you !

というお洒落な返事も嬉しさを大きくさせてもらった。

世界で使われているソフトウエアに貢献できるのであれば、せっかくならば英語ももう少し勉強してみようかなと思ったものだった。

参考文献

著作権譲渡の手続きやメーリングリストへの投稿方法は下記の記事が参考になった。

spacemacsにコントリビュートをした話

実際のプルリクエスト

github.com

背景

2022年の年末頃から、日本語文章の執筆用途としてEmacsを使い始めた。

1から設定を積み上げていくのはそれなりに大変であることが予想されたので、Spacemacsを導入した上で、個別の設定を追加している。

Spacemacsでコード補完の候補を表示するレイヤー(パッケージやデフォルト設定集)はauto-completion layerを使用していて、表示フロントエンドにはcompany-boxを使用している。実際の設定は下記の通り。

     (auto-completion :variables
                      auto-completion-enable-sort-by-usage t
                      auto-completion-use-company-box t
                      auto-completion-enable-help-tooltip 'manual
                      auto-completion-complete-with-key-sequence-delay 0.0
                      auto-completion-enable-snippets-in-popup t
                      auto-completion-minimum-prefix-length 1)

この設定で補完候補を表示した場合に、アイコンと文字列が中央揃えにならない、意図しない画像が表示される、という事象が発生していることに気づいた。

この不具合の原因を簡易版、やや厳密な説明版の2つで説明してみようと思う。

不具合の原因(簡易版)

company-boxで defvar を使って定義されている変数 company-box-icons-all-the-icons をuse-packageの :custom で設定をしようとしていため。

:custom に記述した内容は、パッケージが読み込まれる前に実行されるのだが、company-box-icons-all-the-icons はパッケージ読み込み前には当然未定義であるため、設定が正しく行えない

なおuse-packageは下記の記事でも解説を試みた。

qiita.com

不具合の原因(やや厳密な説明版)

少々込み入っているので、箇条書きで説明をすると、

  1. use-packageには:custom で設定した値が custom file に保存されることを防ぐことができるオプション( use-package-use-theme )がある
  2. 上記のオプションが有効になっている場合 :custom の変数定義は custom-theme-set-variables で行われる
  3. この custom-theme-set-variables は「変数が定義済みの場合には上書き、未定義の場合には指定したテーマのシンボルに対するプロパティの追加のみを行う
  4. 他方、上記でプロパティに設定を追加したテーマ(シンボル名: 'use-package)は常に無効な状態になっている

という、事象の掛け合わせで :custom の設定が反映されない状態になっていた。

厳密な説明を試みたものの正しく説明できている自信はないので「defvar で定義された変数を use-packageの :custom セクションで定義をすると設定が反映されないことがある」くらいに捉えてもらうのがいいと思う。

不具合の解消

パッケージが読み込まれる後に実行される :config セクションに移動し、 setq で上書きするように変更したことで修正が完了した。

spacemacsのこのレイヤーでは、自動テストなどは用意されていなかったため、コード差分と動作確認用のスクリーンショットをつけてプルリクエストを送った。

まとめ

変数がdefvarで定義されていることに気づくのもそれなりに時間がかかったが、それ以上になぜdefvarをuse-packageの:custom で上書きできないのか、という調査に時間を取られた。

ただ、時間をかけて原因追及をしたことで、Spacemacsのレイヤーの仕組み、use-package、変数定義(setq, defvar, defcustom)、マクロ展開、テーマなどの多くの知識を手に入れられた。 また、本記事では触れなかったがedebugを使ったデバッグなど、Elispの不具合発見方法に馴染むことができた。

今後もEmacsの設定や各種パッケージへのコントリビュートをしていこうと思う。