2025-03-12

Android版Emacsのためにした設定

先日Android版のEmacsを導入してみましたが、その後にした設定をまとめました。

これまでの設定:

以下はそれ以外の細かい設定です。

起動したらorgファイルを開く

私はこれまで特に起動画面のカスタマイズなどはしていませんでした。とは言ってもデフォルトのスプラッシュスクリーンくらいはOFFにしていたので(setq inhibit-splash-screen nil)、起動したらscratchバッファが表示される状態でした。

PCならこれで全く困らずそこから必要に応じてファイルやディレクトリを開けば済むわけですが、Androidスマホだと何を開くにも小さなボタンを何回も押さなければならず苦痛です(メニューバーの中のブックマークの位置と来たら……)。

なのでとりあえずデフォルトのorgファイルを最初から開くことにしました。その名も phone.org

とは言ってもやることは init.el の最後でfind-fileするだけです。一応OSとファイルの存在くらいは確認しておきましょうか。

(when (eq system-type 'android)
  (let ((home-file "~/my-org-files/phone.org"))
    (when (file-regular-p home-file)
      (find-file home-file))))

org-modeでメニューを作る

そうして開いたorgファイルにはよくアクセスするファイルやディレクトリへのリンクを書いておくわけです。

しかしそれだけでは足りません。よく使うコマンドもワンタッチで実行できるようにしておきたいところ。そんな時に便利なのが elisp: リンクです。 [[elisp:(message "Hello")][ハロー]] などと書けば押すとEmacs Lispの式が評価されるリンクが作成できます。description(ハローの部分)には画像を指定したりも出来るので工夫次第で綺麗な画面を作ることも出来ることでしょう。他人からもらったorgファイルだと何をされるか分からないので危険なリンクですが、自分が作ったものなら何の問題もありません。一応評価するかyes/noで聞かれるのでキーボードからだとy・e・s・RETと4ストローク必要ですが、Androidであればダイアログのyesをタップするだけです。むしろちょうど良いくらいです。

例えば次のような感じです(適当に似たようなものをでっち上げたので動作未確認)。

#+TITLE: Android用ホームファイル

- [[elisp:(my-ssh-setup)][ssh-agentの起動]]

- [[file:/data/data/com.termux/files/home/][Termux Home]]

- [[file:/data/data/org.gnu.emacs/files/][Emacs Home]]

- [[file:/sdcard/][SDCARD]]

- [[file:~/my-org-files/][Orgファイルたち]]
  - [[elisp:(my-git-pull "~/my-org-files/")][(my-git-pull)]]
  - [[elisp:(my-git-commit-push "~/my-org-files/")][(my-git-commit-push)]]
  - [[file:~/my-org-files/phone.org][phone.org]]
  - [[file:~/my-org-files/todo.org][todo.org]]
  - [[elisp:(org-agenda nil "a")][Agenda]]

- [[file:~/my-emacs-config/][Emacsの設定ファイル]]
  - [[elisp:(my-git-pull "~/my-emacs-config/")][(my-git-pull)]]
  - [[elisp:(magit-status "~/my-emacs-config/")][(magit-status)]]
  - [[file:~/my-emacs-config/init.el][init.el]]
  - [[file:~/my-emacs-config/early-init.el][early-init.el]]

各種ディレクトリへのリンクの他に、Gitのpushやpull、org-modeのアジェンダの表示なんかを入れておくと良いでしょう。これでAndroidからいつでも予定を確認できます。

Gitのためにあらかじめ次のような関数を用意しておきます。

(defun my-git-pull (dir)
  (let ((default-directory dir))
    (vc-pull)))

(defun my-git-commit-push (dir)
  (let ((default-directory dir))
    (unless
        (and (zerop (shell-command "git commit . -m \"Update\"" "*my-git-push*"))
             (zerop (shell-command "git push" "*my-git-push*")))
      (pop-to-buffer "*my-git-push*"))))

~/my-org-files/ の下は同期ソフト代わりにGitを使うようなイメージです。将来的にはある程度自動的にマージ(rebase?)するような仕掛けも欲しい所。

~/my-emacs-config/ の下はある程度ちゃんとコミットメッセージを書くためにとりあえずMagitを起動するリンクを載せておきましたが、Magitはなぜかものすごく遅いのでvc-checkinとvc-pushの方がいいかもしれません。

リンクを開くときに新しいウィンドウを開かない

使っていると何かにつけて新しいウィンドウを開いてくるのが気になります。PCでは分割ウィンドウが不要ならC-x 1を押せば済む話ですが、タッチ操作がメインの場合はウィンドウを閉じるのにも一手間必要です。画面が小さいので分割されるとより見づらくなってしまうということも関係しているのでしょう。

先日の設定(モードラインをドラッグしてウィンドウを消す)でウィンドウを簡単に閉じられるようになったとはいえ、そもそも最初から新しいウィンドウを開かなければいい話です。とは言え別ウィンドウを開くのが必ず悪いかと言われればよく分からないので、とりあえず気になったところだけ直すことにします。display-buffer-alistあたりを変更しようかとも思いましたが、とりあえず個々のコマンドの設定を変更してみます。

;; org-modeのリンクを開くときに別ウィンドウを開かない
(setf (alist-get 'file org-link-frame-setup) 'find-file)

;; org-agendaで別ウィンドウを開かない
(setq org-agenda-window-setup 'current-window)

;; Dired内でファイルをタップしたときに別ウィンドウを開かない
(define-key dired-mode-map [mouse-2] #'dired-mouse-find-file)

;; Diredから他のディレクトリを開くときに元のDiredバッファをkillする
;; (`dired-mouse-find-file'の挙動に影響する。
;;   dired-mouse-find-alter-fileは存在しない)
(setq dired-kill-when-opening-new-dired-buffer t)

おそらく他にも同じように感じる場所があると思いますが、気がついたら逐一設定していくことにします。

org-captureの保存先

ディレクトリ構成が変わったのでorg-captureの保存先も変える必要があります。……と思ったのですが、その後 ~/ とシンボリックリンクを組み合わせてPCと同じパスになるようにしてしまったので設定は不要になりました。

ファイルの同期

PCではファイルの保存やEmacsの終了のタイミングでファイルを同期するような仕組みを整備していましたがスマホでは止めておきました。電波が入らないところで使っている可能性があるので。とりあえず手動で同期しようと思います。

物理キーボードからのIMEのON/OFF

Bluetoothのハードウェアキーボードを接続してみたのですが、IMEのON/OFFの方法がよく分かりませんでした。半角/全角を押すと切り替わるように見えてOFFなのにM-<やM->を押すと<や>が入力されてしまったり、かと思えばそれらはコマンドとして認識されるけど半角/全角切り替えはできなかったり。

調べてみると、これはどうもtext-conversion-styleoverriding-text-conversion-styleが影響しているようです。

text-conversion-styleはIMEの挙動を指定するバッファローカル変数です。nilのときIME無効、非nilのとき有効になるようです。非nilの中でも、シンボル action やシンボル password といった指定もあります。ザッと検索してみたところ、text-mode(org-modeはここに含まれます)やprog-mode(一般的なプログラミング用のモードはここに含まれます)では tcomint-mode(shell-mode等がここに含まれます)やminibuffer-modeでは actionread-passwdでは password が指定されているようでした。つまりバッファ毎にそのバッファに入力される文字の種類・性質を指定するという側面があるようです。

それでtext-conversion-styleがnilのバッファではIMEを介さない入力が可能で、tのバッファでは常にIMEを介した入力になってしまうということのようです。極端だってば。(注: この辺りの挙動はIMEによっても若干異なるようです。Gboardはtext-conversion-styleがnilの時でもGboardのショートカットキーを完全に無効にすることはできませんでした。M->を押すと何か不可解なエラーが出たり、M-tを押すとGoogle翻訳を使おうとしたりします。ATOK Passport Proはショートカットキーが干渉することはありませんでしたが、text-conversion-styleがtのときは半角文字もIME経由でバッファへ直接挿入されるためorg-modeのスピードコマンドやedebugのアルファベット1文字のキーが使えなかったりしました)

text-conversion-styleの効果はグローバル変数overriding-text-conversion-styleで上書きできます。デフォルトの値はシンボル lambdatext-conversion-styleの値を尊重します。それ以外の値が指定されている場合は、text-conversion-styleは無視してoverriding-text-conversion-styleの値が使われるようです。

ということはこのoverriding-text-conversion-styleを切り替えるコマンドを作ればIMEの挙動をユーザーが明示的に指定出来るようになるはずです。

(defun my-toggle-text-conversion-style ()
  "`overriding-text-conversion-style'を`lambda'とnilとの間で切り替えます。"
  (interactive)
  (cond
   ((eq overriding-text-conversion-style 'lambda)
    (setq overriding-text-conversion-style nil)
    (set-text-conversion-style text-conversion-style))
   ((null overriding-text-conversion-style)
    (setq overriding-text-conversion-style 'lambda)
    (set-text-conversion-style text-conversion-style))))

それを私はCtrl+変換に割り当てました。私は普段PCでもこのキーでIMEのON/OFFを切り替えているので。

(define-key global-map [C-henkan] #'my-toggle-text-conversion-style)

半角/全角は英数/カナ切り替え(カナロック)のようなものだと考えることにします。

ちなみにハードウェアキーボードの細かいレイアウトはshiftrot/caps2ctrlkcmファイルを独自にカスタマイズして調整しています。

line-spacingや文字サイズの調整

指で位置を指定することを考えるとline-spacingは大きめが良いでしょうね。使うフォントにもよると思いますが。文字サイズは視力との兼ね合いでしょうか。

おしまい

これでAndroid版のEmacsを触って最初に思いついた設定は一通り終わりました。

最初はAndroid版のEmacsなんて使い物になるの~? と疑っていましたが、思っていた以上に使える道具だということが分かってきました。Orgzlyはもう私には必要ありません。Android用に作られた昔からあるテキストエディタアプリも用済みです。タッチ操作で普通に編集できるので遜色ありません。長押しメニューが圧倒的に便利です。Dropbox等のクラウドストレージとの連携が必要な人だとまだその手の機能を搭載したアプリの方に分があるかもしれません……ってTrampのrcloneメソッドがあるんですね(使ったことはありません)。

すごいのはほぼ全ての設定がPCと同一だということです。Androidのためだけに設定したことは、これまでに紹介したものしかありません。

もちろん今後も触っていて思いついた改善をしていこうと思います。決して尽きることはないでしょう。