【Emacs】テンプレート用パッケージTempelの設定

テンプレート挿入・展開に便利なパッケージTempel がある.

github.com

公式を参考に,設定はこんな感じ.テンプレート展開の部分キーワードを入力して M-+ でテンプレートが展開するので便利である. また「ここにテンプレートを展開したい」と思ったところで M-* でテンプレートを選択して展開することもできる.

;; テンプレート展開をトリガーするためのプレフィックス
;; テンプレート挿入モードに入るためのキー,という認識
;; デフォルトはnil,つまり地のソースコードとテンプレート挿入の境界がない
;; (setq tempel-trigger-prefix "<") 

(global-set-key (kbd "M-+") 'tempel-complete) ;; テンプレートキーワード途中までの入力内容で補完し,挿入
(global-set-key (kbd "M-*") 'tempel-insert) ;; 何も書かれていないところにテンプレート挿入

;; completion-at-point にテンプレート展開を追加
(defun tempel-setup-capf ()
  (setq-local completion-at-point-functions
              (cons #'tempel-expand  ;; exact match; キーワードに厳密にマッチしたときに発動
                    completion-at-point-functions)))

;; テンプレート展開を適用するメジャーモードを指定
(add-hook 'prog-mode-hook 'tempel-setup-capf) ;; プログラミング系全般
(add-hook 'text-mode-hook 'tempel-setup-capf)

;; eglotやcapeと連携させる例
;; tempel-complete にすると入力途中でもテンプレート候補が出てくるようになる
;; (defun tempel-setup-capf ()
;;   (setq-local completion-at-point-functions
;;               (list (cape-capf-super
;;                      #'tempel-complete
;;                      #'eglot-completion-at-point)
;;                     #'cape-keyword
;;                     #'cape-dabbrev
;;                     #'cape-file)))
;; (add-hook 'eglot-managed-mode-hook #'tempel-setup-capf)
;; このとき,(add-hook 'prog-mode-hook 'tempel-setup-capf) は余分なのでコメントアウトしておくこと

;; フィールド移動のキーバインドを追加
(with-eval-after-load "tempel"
  (define-key tempel-map (kbd "<tab>") #'tempel-next)
  (define-key tempel-map (kbd "C-i") #'tempel-next)
  (define-key tempel-map (kbd "<backtab>") #'tempel-previous)
  (define-key tempel-map (kbd "C-S-i") #'tempel-previous)
  (define-key tempel-map (kbd "C-<tab>") #'tempel-previous))

tempel自体はテンプレート展開の枠組みを提供しているだけなので,テンプレートは自分で用意しなければならない. 足りないものは自分でテンプレートを書いて追加していけばよい.例えばC言語の場合は以下のようなテンプレートを書いておくのも良いだろう.記述のルールはTempelの公式リポジトリに書いてあり,簡単である. オリジナルのテンプレートファイルの置き場所は ~/.emacs.d/templates となっている. (2024/06/29)

c-mode
(io "#include <stdio.h>" n)
(lib "#include <stdlib.h>" n)
(str "#include <string.h>" n)
(math "#include <math.h>" n)
(main "int main(int argc, const char *argv[]) {"n> q n> "return 0;" n "}")
(for "for (" (p "i") "; " p "; " p ") {"n> q n "}" >)
(switch "switch (" p ") {"n> "case " (p "1") ":" n> q n> "break;\n" "\t\tdefault:"n >n "break;"> n "}" >)
(while "while (" p ") {"n> q n "}" >)
(struct "struct " p " {" n> q n "};")
(printf "printf(\"" p "\\n\"" p ");" q)
(scanf "scanf(\"" p "\", " p ");" q)

pythonだとこんな感じだろうか.

python-ts-mode
(for "for " p " in " p ":" n> q)
(from "from " p " import " q)
(if "if " p ":" n> q)
(ife "if " p ":" n> p n> "else:" n> q)
(ifmain "if __name__ == '__main__':" n> q)
(import "import " p q)
(init "def __init__(self" p "):" n> q)
(init_docstring "def __init__(self" p "):" n> "\"\"\"" p "\"\"\"" n> q)
(np "import numpy as np" n> q)
(main "def main():" n> q)
(method "def " p "(self" p "):" n> q)
(while "while " p ":" n> q)
(class "class " p "(" p "):" n> q)

eglot-completion-at-pointcompletion-at-point-functions に含めるとヒットする補完候補が多すぎるケースがしばしばあるので,tempel-setup-capf を少し修正して思い切って制限するのもアリ.

(defun tempel-setup-capf ()
  (setq-local completion-at-point-functions
              (list #'tempel-complete
                    #'cape-keyword
                    #'cape-dabbrev
                    #'cape-file)))

参考

Eglot でのPython開発環境 - makoのノート