🤌

Emacsで . を打ったときにいい感じにする

2023/07/01に公開

プログラミング言語の構文が野暮ったいからといって、人間様がそれにつきあってキーボードで愚直にカチカチと丹精込めて入力して差し上げる必要はない。

たとえば、JavaやRubyに慣れた人にとってはPHPのオブジェクトアクセスの->は、とても野暮ったく見えるだろう。PHPの.演算子は文字列結合だというのも罠だ。

前段: Smartchrを使う

smartchr.elという拡張パッケージがある。これはキーを繰り返し押すと別の文字が入力されるという優れものだ。

https://github.com/imakado/emacs-smartchr

基本的な使いかたはこうだ。

(bind-key "@" (smartchr "@" "$this->") php-mode-map)

@を一度押すと@が、そのまま続けて@を押すと$thisに置き換わる。さらに@を連打すると、@$this->がトグルし続ける。$this->$thisでもいいのだが、メソッド呼び出しをせずに$thisを使うシチュエーションというのもそんなに多くないだろう。必要ならバックスペースを二回押せばいいと思える頻度だ。

さらにはこのようなキーも登録できる。

(bind-key "@" (smartchr "$this->" "@" "@@") php-mode-map)

これは@を押すといきなり$this->が入力され、続けて押すと@に、さらに押すと@@、もう一度押すと$this->になる。どちらが便利か、しっくりくるかは個人の好みや普段触っているPHPプロジェクトの性質にもよるだろうが、PHPの@(エラー制御演算子)を使うべき場面というのもさして多くないということは留意していいだろう。

さらにSmartchrではこのようなテンプレートも使える。

(bind-key "[" (smartchr "[`!!']" "array(`!!')" "[[`!!']]") php-mode-map)

文字列の中に`!!'と書くと、カーソルがそこに移動する。レガシーコードでarray()を使わざるを得なかったとしても[を連打するだけで簡単に入力できる。さらにもう一度入力すれば[[]]になるという塩梅だ。

本題

. だ。われわれはこれまで不便に飼い慣らされていたが、一度意識してみると利用頻度が多い割に->は入力しにくい。右手を上段まで伸ばした上で下段に戻ってShiftまで押さなければいけない。

PHPでは文字列結合をするよりも、メソッド呼び出しやプロパティにアクセスする頻度は圧倒的に高い。もっというと演算子として使うなら.の次にはスペースも入るので、それもいっしょに挿入してしまうと便利だろう。

(bind-key "." (smartchr "->" ". " ".."))

AIによって入力補完が効く世の中になったとはいえ、オブジェクトのメンバーにアクセスするときに->とわざわざタイプしなくて良くなるのはすこぶる便利だ。なぜもっと前にこの設定をやらなかったのだ。私の10年間をどぶに捨てた気分だ。

しかし、大筋満足ではあるのだが、たまに不便さが目立つことも否定しがたい。

  • 文字列結合をしたいとき
    • 変数だけなら$a . $b . $cではなく"{$a}{$b}{$c}"と書いた方がよい
    • ただし、これは変数でないものは埋め込めないので定数などを結合するときに . を避けがたい
  • 文字列の中でファイル名を書きたいとき
    • /path.jsonと書きたいのに/path->になる

ということで、$path = __DIR__ . '/path.json'; って書きたいのに二回ひっかかるのは、全体的な頻度として多くはないもの、結構な頻度でひっかかるのでストレスだ。改善しよう。

補助関数を用意する

PHPでカーソル位置が何であるかを判断して、何を入力するかを判断します。たとえば現在位置が「文字列リテラルの中か」「文字列リテラルの直後か」「それ以外か」としましょう。それぞれの場合に何を入力するかを指定するヘルパーを作ります。

使いかたはこうです(no-string within-string-or-comment next-to-string)の順で指定

  (bind-key "." (smartchr
                 (my-php-smartchr-dot "->" "." ". ")
                 (my-php-smartchr-dot ". " ".." "..")
                 "...")
            php-mode-map)

文字列またはコメントの中身だったら.はそのまま、文字列の直後だったら. .., ...の順でトグル、それ以外では->, . , ...としましょう。

さて、実装していきましょう。

;;;###autoload
(defun my-php-smartchr-dot (no-string within-string-or-comment next-to-string)
  "Make a smartchr to press `.` key in PHP with NO-STRING, WITHIN-STRING-OR-COMMENT and NEXT-TO-STRING."
  (let ((select-template (lambda ()
                           (save-excursion
                             (cond
                              ((php-in-string-or-comment-p) within-string)
                              ((and (c-backward-token-2 1 nil)
                                    (goto-char (1+ (point)))
                                    (php-in-string-p))
                               next-to-string-or-comment)
                              (no-string))))))
    (smartchr-make-struct
     :cleanup-fn (lambda () (delete-char (- (length (funcall select-template)))))
     :insert-fn (lambda () (insert (funcall select-template))))))

(php-in-string-or-comment-p)と(php-in-string-p)はsyntax-tableから判断していますが、(c-backward-token-2 1 nil)はCc Modeの機能です。

まとめ

入力補完(オートコンプリート)がそうであるように、プログラミングは入力したいプログラムをそのままキーボード入力しなければいけないということはありません。ガンガン省力化していきましょう。キミのよく書く言語でいい感じのキーコンボを編み出したら教えてね。

Discussion