Emacs Advent Calendar 2022 3日目の記事です。Zenn に書きました。
ひさしぶりに Emacs Lisp を書きました。ググりながら10分くらいで書いた適当なもの。
たとえば、全部小文字で書いたSQLを後から予約語だけ大文字にしたい、みたいなときに使う想定です。
フェイスが font-lock-keyword-face
になっている箇所を大文字にしているので、SQL の場合は sql-mode になっている状態で実行します。
(progn (goto-char (point-min)) (while (not (eobp)) (let ((faces-at-point (get-char-property (point) 'face))) (when (eq 'font-lock-keyword-face faces-at-point) (upcase-word 1)) (forward-char 1))))
Emacs で SQL 書く → リージョン選択(現在位置から先頭に移動して末尾に移動) → コピー
というのを何回も繰り返す場合、
というあたりがだるかったので改善してみた。
(defun my-sql:query-beginning () (let (beg beg-para beg-sc) (save-excursion (backward-paragraph) (setq beg-para (+ (point) 1))) (save-excursion (when (search-backward-regexp ";\n" nil t) (setq beg-sc (+ (point) 2)))) (if beg-sc (max beg-para beg-sc) beg-para))) (defun my-sql:query-end () (let (end end-para end-sc) (save-excursion (forward-paragraph) (setq end-para (- (point) 1))) (save-excursion (when (search-forward-regexp ";\n" nil t) (setq end-sc (- (point) 1)))) (if end-sc (min end-para end-sc) end-para))) (defun my-sql:copy-current-query () "Copy current query." (interactive) (kill-ring-save (my-sql:query-beginning) (my-sql:query-end))) (global-set-key (kbd "C-M-h") 'my-sql:copy-current-query)
まず、今いる位置から動かずに1キーストロークで段落選択してコピーできるようにしてみた。mark-paragraph 使えば簡単。
(defun copy-current-query () "..." (interactive) (save-excursion (mark-paragraph) (kill-ring-save (region-beginning) (region-end))))
これの不満点:
(defun copy-current-query () "..." (interactive) (let (beg end) (save-excursion (backward-paragraph) (setq beg (+ (point) 1)) (forward-paragraph) (setq end (- (point) 1)) (kill-ring-save beg end))))
まだ不満な点:
選択の単位として段落を使うと、たとえば1行ずつのSQLをこのように並べて書いていて
select * from table1 limit 10; select * from table2 limit 10; select * from table3 order by created_at desc limit 10;
真ん中のクエリだけコピーしたいのに3行ともコピーされてしまう。1行ごとに改行入れればよいけど、ワンライナーのために無駄に行を消費したくない。
そこで、区切りとして前方、後方の ";\n" を探すようにしてみる。
セミコロン書かない+段落で見れば良いというケースもあるので、段落境界と比較して近い方を使うようにする。
(defun copy-current-query () "..." (interactive) (let (beg beg-para beg-sc end end-para end-sc) (save-excursion (backward-paragraph) (setq beg-para (+ (point) 1)) (forward-paragraph) (setq end-para (- (point) 1))) (save-excursion (if (search-backward-regexp ";\n" nil t) (setq beg-sc (+ (point) 2)) (setq beg-sc beg-para))) (save-excursion (if (search-forward-regexp ";\n" nil t) (setq end-sc (- (point) 1)) (setq end-sc end-para))) (setq beg (max beg-para beg-sc)) (setq end (min end-para end-sc)) (kill-ring-save beg end)))
大体いい感じになった。
選択範囲の始点・終点を求める部分は使いまわせそうなので、関数を抽出する。こうしておくと、たとえば、今いるクエリをフォーマットする場合なんかに流用できる。
関数が複数になったので名前空間的に my-sql: というプレフィックスを付けてみた。
;; 上の完成形と同じなのでコード省略
関数ごとに目的がはっきりしているし、良いと思う。
以下おまけ。自分用設定的なもの。
視覚的フィードバックが何もないと不安になるので、どこをコピーしたのか分かるように一定時間ハイライトさせる。
(一定時間だけ指定範囲をハイライトする Emacs Lisp | anobota を使用)
(require 'volatile-highlight) (defun my-sql:copy-current-query () "..." (interactive) (let ((beg (my-sql:query-beginning)) (end (my-sql:query-end))) (kill-ring-save beg end) (volatile-highlight beg end 1)))
フォーマット。
参考: (書きかけ)EmacsでSQLの整形(要Ruby) | anobota
(defun my-sql:format-currenty-query () "Format current query" (interactive) (shell-command-on-region (my-sql:query-beginning) (my-sql:query-end) (format "ruby %s" anbt-sql-formatter:formatter-path) nil t) (volatile-highlight (my-sql:query-beginning) (my-sql:query-end) 0.2)) (global-set-key (kbd "C-S-f") 'my-sql:format-currenty-query)
How do I turn off vhdl-mode in emacs? - Stack Overflow
http://stackoverflow.com/questions/1361785/how-do-i-turn-off-vhdl-mode-in-emacs
この2つのどちらかが有効だとだめだった。
(prefer-coding-system 'utf-8) (set-default-coding-systems 'utf-8)
emacs-version ;=> "23.3.1" たしかgnupackで入れたもの
setq でいいんだっけ……と思いつつ以下を .emacs に追加。
(setq anything-c-source-files-in-current-dir+ '((name . "Files from Current Directory") (candidates . (lambda () (with-current-buffer anything-current-buffer (directory-files (anything-c-current-directory) t "[^~]$")))) (candidate-transformer anything-c-highlight-files) (type . file)))
anything-config.el から該当箇所をコピペして directory-files の第3引数の正規表現を追加しただけ。
修正前:
(set-frame-font "Takaoゴシック-10")
これで新しいフレームを開くと、普通に Emacs を起動したときよりも大きいフォントで表示される。
それは困るというか嫌なのでちょっとぐぐって次のように修正したら解決した。
;; 下のを追記したらこっちは不要っぽい ;; (set-frame-font "Takaoゴシック-10") (setq default-frame-alist (append '((font . "Takaoゴシック-10")) default-frame-alist))
emacsclient で新しいフレームを開いたときもこれで大丈夫でした。
#+OPTIONS: ^:{}
で解決した(「ハット ^ で上付き」も無効になる)。
上のように指定した場合、
_{下付きにしたい文字列}
で下付きにできる(上付きも同様)。
で確認した。
ハイライトしたい範囲の開始ポイントと終了ポイントを指定すると 一時的に指定範囲をハイライトする。 ハイライトする時間はデフォルトは 0.1秒なので、一瞬キラっとする感じ。
;; require する (require 'volatile-highlight) ;; 使う (volatile-highlight 1 10) ; 開始位置、終了位置 (volatile-highlight 1 10 2) ; 開始位置、終了位置、ハイライトさせる時間(秒)
ハイライトに使っているフェイスは volatile-highlight-face
で、たとえば背景色を変えたい場合は
(set-face-background 'volatile-highlight-face "#ff8")
のように指定する。
デフォルトのハイライト持続時間を変更したい場合は volatile-highlight:default-duration-sec
に秒数をセットする。
(setq volatile-highlight:default-duration-sec 10)
eval-defun または eval-last-sexp して一瞬キラッとさせたい場合の例:
(require 'thingatpt) ; for beginning-of-sexp (defadvice eval-defun (after blink-after-eval-defun activate) (let (beg end) (save-excursion (end-of-defun) (forward-char -1) (setq end (point)) (beginning-of-defun) (setq beg (point))) (volatile-highlight beg end 0.2))) (ad-activate 'eval-defun) (defadvice eval-last-sexp (after blink-after-eval-last-sexp activate) (let (beg end) (save-excursion (setq end (point)) (beginning-of-sexp) (setq beg (point))) (volatile-highlight beg end 0.2))) (ad-activate 'eval-last-sexp)
修正しました。変更点は以下の通り。
修正点:
なるべく .emacs.el の方で挙動を変えられるようにして、そのために必要な修正を imcap.el に加えるかたちになっています。
元の imcap.el のライセンスが不明なのでとりあえず diff を貼っときます。
diff:
.emacs.el: