Emacs
概要
Emacsは非常に優れた拡張性を特徴とするテキストエディタ。 エディタに内蔵された言語Emacs Lispで柔軟に設定を変更できる。
できること。
- 情報集積(org-mode, Org-roam)
- タスク管理(org-mode)
- Git(magit) + GitHub(magit-forge)
- コマンド実行と結果出力(org-mode)
- ウィンドウマネージャ(EXWM)
- 静的サイトジェネレータ(org-mode)
- ブラウザ(eww)
- インターネットラジオプレーヤ(eradio)
- フィードリーダー(elfeed.el)
- ファイラ
- 表計算
- テスト実行(rspec-mode)
- ターミナル実行(vterm)
- Docker管理(docker-mode)
- リマインダー(org-alert)
- PDF閲覧(pdf-tools)
の機能を、共通の効率的なキーボード操作、プレーンテキストベースで行える。
自分の文芸的設定はkijimad Emacs Configにまとめてある。
Memo
guix でビルドする
ビルドしてからパッケージを登録する。
guix build emacs@29.1 guix package -i emacs@29.1 guix package --roll-back # ロールバックしたいとき
doom-modelineでアイコンが表示できないのを直す
(all-the-icons-install-fonts)
しても直らないので困っていた。nerd-fontsでフォントを追加すると表示されるようになった。良い。
インストール方法のメモ。
$ git clone --depth 1 git@github.com:ryanoasis/nerd-fonts.git $ ./install.sh
コンパイルする
Ubuntu 18.04.5 で Gcc Emacs をビルドする | Braindumpを参考にコンパイルする。
ソースコードをクローンしてくる。
$ git clone --single-branch --depth=1 https://github.com/emacs-mirror/emacs.git
.configureを生成する。
$ ./autogen.sh
configureを実行する。
$ ./configure --with-native-compilation=aot --with-modules --with-x && \ make && \ sudo make install
--with-native-compilation
でelispをネイティブコンパイルして高速化する--with-x
でX Windowで動かすようにする
2つのregionを比較する
ediff-regions-wordwise
を使う。
エラー時に表示する
M-x toggle-debug-on-error
で、エラー時にトレースバッファが表示されるようになる。デバッグがしやすい。
subrを探索する
使用頻度の高いマクロが多数定義されている。
- thread-first
- defsubst
- named-let
mermaidをorg-babelで実行する
- mermaidインストールが面倒なのでDockerでやりたい
- mermaid-modeは対応してるのだが、org-babelでのコマンドが間違っているので関数を修正
- 画像は保存して、orgファイルにインライン表示したい
- 名前がかぶって消されるとやっかいなので、ランダム文字列を自動生成する関数を作成
UbuntuでEmacsをビルドする
System Crafters Live! - August 20, 2021 - System Craftersを参考に、ローカルマシン(Ubuntu)でビルドする。
sudo apt install ncurses-dev gnutls-bin
あとはINSTALL.repoに乗っているコマンドを実行する。
./autogen.sh ./configure make
./src/emacs
コマンドを再実行する
C-x z (repeat)
で直前のコマンドを再実行できる。
C-x M-: (repeat-complex-command)
で直前のコマンドをS式表示しつつ実行できる。
TODO 覚えるコマンド
移動に便利なコマンド。
- M– マイナスの前置引数
- 例えばC-M– C-f は1文字戻る
- M– M-d 前の単語を削除
- M– C-k kill-lineの逆、行の左側を削除
- C-M-d, C-M-u S式の中、外に移動する
- C-M-n, C-M-p 次のリスト、前のリストに移動する
- C-M-k S式を削除する
- M-}, M-{ パラグラフの最後、最初に移動する
- M-a, M-e センテンスの先頭、最後に移動する
- C-M-a, C-M-e defunの先頭、最後に移動する
- C-M-v 別ウィンドウを下方にスクロール
- M-r ポイントを左上、左、左下に移動
削除。
- M-k センテンスを削除
- C-M-k S式を削除
- C-S-<backspace> 現在の行を削除
ブックマーク。
- C-x r m ブックマークをセット
レジスター。
- C-x r s レジスターにリージョンを保存
- C-x r SPC レジスターにポイントを保存
- C-x r j レジスターにジャンプ
- C-x r i レジスターの内容を挿入
マーク。
- M-h 次のパラグラフをマーク
- C-M-h 次のdefunをマーク
- C-x h バッファー全体をマーク
- C-x C-p 次のページをマーク
- M-@ 次の単語をマーク
- C-M-@ 次のS式をマーク
検索。
- C-M-s regexpインクリメンタル検索を開始する
キルリング。
- C-M-w 次のコマンドがkillコマンドなら、killリングに追加する
入れ替え。
- C-x C-t 行の入れ替え
コメント。
- C-x C-; 行をコメント化あるいは非コメント化
- M-x comment-box リージョンを囲み枠でコメント化する
大文字化。
- M-c 次の単語をcapitalize(Word)
- M-u 次の単語を大文字に(WORD)
- M-l 次の単語を小文字に(word)
現在実行しているコマンドが入る変数
this-command
リンクを開くときにブラウザにフォーカスさせない
firefox側で設定する。about:config をURLバーに打ち込み、設定項目 browser.tabs.loadDivertedInBackground を true にする。 https://stackoverflow.com/questions/10506496/run-browse-url-in-emacs-without-giving-focus-to-the-browser
バッファ管理
rails開発で処理が多くのファイルに分散していて、同じような名前のファイルが多い場合、集中のスイッチングコストが大きくなる。今まではあまりファイル分散してなかったので counsel-switch-buffer
ひとつで問題なかったが、タスクやコードの構造によって問題になりうる。ファイル移動しようとするたびに思考が中断される。
- 方法1: projectile-railsのキーバインドを覚える https://qiita.com/yoshinarl/items/8e3c4b075a181f224591
- projectile-rails-find-current-spec(C-c r P) 対応するテストファイルを開く
- MVC系の移動 C-c r M、C-c r C、C-c r V
- 方法2: バッファをリセットする
- 無関係な似たような名前のバッファが溜まるのが問題であって、そもそもこまめにリセットすれば見やすくなる説
- 方法3: ブックマーク
- 必要なものはブックマークしておく。メンドイ。
- 方法4: タブ表示
- ブラウザのように、タブに表示する。結局ファイル名が長かったり、ファイル名が多くなるとやりにくくなるのだが、直近3つくらいのファイルが見られるだけでも改善する
良いページを発見した。Buffer management - WikEmacs
リストの重複を削除する
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/lisp/subr.el#L731-L751
(defun delete-dups (list) “Destructively remove `equal’ duplicates from LIST. Store the result in LIST and return it. LIST must be a proper list. Of several `equal’ occurrences of an element in LIST, the first one is kept. See `seq-uniq’ for non-destructive operation.” (let ((l (length list))) (if (> l 100) (let ((hash (make-hash-table :test #’equal :size l)) (tail list) retail) (puthash (car list) t hash) (while (setq retail (cdr tail)) (let ((elt (car retail))) (if (gethash elt hash) (setcdr tail (cdr retail)) (puthash elt t hash) (setq tail retail))))) (let ((tail list)) (while tail (setcdr tail (delete (car tail) (cdr tail))) (setq tail (cdr tail)))))) list)
100以下の処理
リストの長さによって処理が分岐していることがわかる。まず100行以下での処理を見る。
(let ((tail list)) (while tail (setcdr tail (delete (car tail) (cdr tail))) (setq tail (cdr tail))))
これで重複削除になるのか、と不思議な感じがする。不明な関数を調べる。
setcdr
Setcdr (GNU Emacs Lisp Reference Manual)
setcdrが非常に重要な処理をしているように見えるので調べる。
(setq x '(1 2 3))
(setcdr x '(4))
x
(1 4)
コンスセルのcdrを変更することで、他のリストに置き換える働きをする。
delete
(setq x '(1 2 3 4))
(delete x '(3))
x
(1 2 3 4)
処理に戻る
本筋に戻る。
(delete (car tail) (cdr tail))
は、値を決めて(car tail)、それをリストから取り除く。 例) ’(1 1 2 3) -> ’(2 3) になる。carの1を取り上げて、cdrに含まれる1を削除する。
(setcdr tail (delete (car tail) (cdr tail)))
そしてsetcdrによって、1つになった値と残りの値を連結する。 ’(1) + ’(2 3) -> ’(1 2 3)
(setq tail (cdr tail))
次の値を計算するために、単独になった値を除いたリストにする。 ’(1 2 3) -> ’(2 3)
ここでは、tailだけが次の計算に向けて再代入されたのであって、値の元になったlistは再代入されずに’(1 2 3)が入ったままになっている。すべての計算が終わったあとlistを評価すれば、望みの値が得られる。tailはループ用で、最終的にnilになる。
ハッシュテーブルを使うバージョン
リストの要素が100より大きいとき、ハッシュテーブルを使う実装になっている。100は何基準だろうか。
(let ((hash (make-hash-table :test #'equal :size l)) (tail list) retail) (puthash (car list) t hash) (while (setq retail (cdr tail)) (let ((elt (car retail))) (if (gethash elt hash) (setcdr tail (cdr retail)) (puthash elt t hash) (setq tail retail)))))
- ハッシュにすでに入っている値であれば、その要素を取り除いて次の探索へ
- ハッシュに入っていなければ登録して次の探索
この方法だとループは多いけど、メモリ消費がとても少ないということか。一度のループで比較するのは最初の要素とハッシュの要素だけだ。
インライン展開するdefsubst
Emacs Lisp defsubstで定義した関数はbyte compileのときにインライン展開される。つまり、コンパイルして関数実行時の関数呼び出しのコストがなくなり高速になる。頻繁に用いられる小さな関数で有効。
(defun plus2 (x) (+ x 2)) (byte-compile 'plus2) (defun foo (lst) (plus2 lst)) (byte-compile 'foo)
#[(lst) \301!\207 [lst plus2] 2]
(defsubst plus2 (x) (+ x 2)) (byte-compile 'plus2) (defun foo (lst) (plus2 lst)) (byte-compile 'foo)
#[(lst) “\211\302\\)\207” [lst x 2] 3]
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/lisp/emacs-lisp/byte-run.el#L480-L495
(defmacro defsubst (name arglist &rest body) “Define an inline function. The syntax is just like that of `defun’.
\(fn NAME ARGLIST &optional DOCSTRING DECL &rest BODY)“ (declare (debug defun) (doc-string 3) (indent 2)) (or (memq (get name ‘byte-optimizer) ’(nil byte-compile-inline-expand)) (error ”`%s’ is a primitive“ name)) `(prog1 (defun ,name ,arglist ,@body) (eval-and-compile ;; Never native-compile defsubsts as we need the byte ;; definition in `byte-compile-unfold-bcf’ to perform the ;; inlining (Bug#42664, Bug#43280, Bug#44209). ,(byte-run–set-speed name nil -1) (put ‘,name ‘byte-optimizer ‘byte-compile-inline-expand))))
編集関数ファイルsimple.el
/lisp/simple.elには基本的な編集関数がある。
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/lisp/simple.el#L1
;;; simple.el — basic editing commands for Emacs -- lexical-binding: t --
正規表現置換
入れ替えとかしたい。
検索結果に対する置換 counsel-git-grep-query-replace
counsel-git-grep したあとに counsel-git-grep-query-replace(M-q)することで、git grepした結果に対して置換をかけられる。
message関数はどう動いているか
message関数はメッセージをmessageバッファ、モードラインに表示する関数である。フォーマットを指定できる機能もある。これはどうやって動いているのだろうか。Lispの形をしたC言語で定義されている。
https://github.com/kd-collective/emacs/blob/d983e080e027bd7b680b1e40ccfa0c71d6a3cd94/src/editfns.c#L2849-L2884
DEFUN (“message”, Fmessage, Smessage, 1, MANY, 0, doc: /* Display a message at the bottom of the screen. (ptrdiff_t nargs, Lisp_Object *args) { if (NILP (args[0])
(STRINGP (args[0]) |
&& SBYTES (args[0]) == 0)) { ;; 最初の引数(表示文字列)がないときはクリア message1 (0); return args[0]; } else ;; 引数があるとき、オプションをLisp Objectにして関数に渡す { Lisp_Object val = Fformat_message (nargs, args); ;; フォーマット message3 (val); ;; 表示 return val;; } }
文字列のときはそのまま表示し、フォーマットが必要なときはフォーマットする、ということか。message3とmessage1の違いは何。この関数名の意味。
https://github.com/kd-collective/emacs/blob/d983e080e027bd7b680b1e40ccfa0c71d6a3cd94/src/xdisp.c#L11588-L11608
void message3 (Lisp_Object m) { clear_message (true, true); / メッセージ削除 cancel_echoing (); / エコーエリアをリセット
* First flush out any partial line written with print. * message_log_maybe_newline (); if (STRINGP (m)) { ptrdiff_t nbytes = SBYTES (m); bool multibyte = STRING_MULTIBYTE (m); char *buffer; USE_SAFE_ALLOCA; SAFE_ALLOCA_STRING (buffer, m); message_dolog (buffer, nbytes, true, multibyte); // 本筋の処理 SAFE_FREE (); } if (! inhibit_message) message3_nolog (m); }
本筋はmessage_dologを呼ぶこと。ほかはそのための準備とか、オプションに対応してる部分。
https://github.com/kd-collective/emacs/blob/d983e080e027bd7b680b1e40ccfa0c71d6a3cd94/src/xdisp.c#L11693-L11697
void message1 (const char *m) { message3 (m ? build_unibyte_string (m) : Qnil); }
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/src/xdisp.c#L11430-L11435
void message_log_maybe_newline (void) { if (message_log_need_newline) message_dolog (“”, 0, true, false); }
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/src/lisp.h#L5292-L5294
#define USE_SAFE_ALLOCA \ ptrdiff_t sa_avail = MAX_ALLOCA; \ specpdl_ref sa_count = SPECPDL_INDEX ()
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/src/lisp.h#L5319-L5325
* SAFE_ALLOCA_STRING allocates a C copy of a Lisp string. *
#define SAFE_ALLOCA_STRING(ptr, string) \ do { \ (ptr) = SAFE_ALLOCA (SBYTES (string) + 1); \ memcpy (ptr, SDATA (string), SBYTES (string) + 1); \ } while (false)
https://github.com/kd-collective/emacs/blob/30cf1f34c583d6ed16bdc5b9578370f30c95fe1b/src/xdisp.c#L11438-L11633
/* Add a string M of length NBYTES to the message log, optionally terminated with a newline when NLFLAG is true. MULTIBYTE, if true, means interpret the contents of M as multibyte. This function calls low-level routines in order to bypass text property hooks, etc. which might not be safe to run.
This may GC (insert may run before/after change hooks), so the buffer M must NOT point to a Lisp string. */
void message_dolog (const char *m, ptrdiff_t nbytes, bool nlflag, bool multibyte) { const unsigned char *msg = (const unsigned char *) m;
if (!NILP (Vmemory_full)) return;
if (!NILP (Vmessage_log_max)) { struct buffer *oldbuf; Lisp_Object oldpoint, oldbegv, oldzv; int old_windows_or_buffers_changed = windows_or_buffers_changed; ptrdiff_t point_at_end = 0; ptrdiff_t zv_at_end = 0; Lisp_Object old_deactivate_mark;
old_deactivate_mark = Vdeactivate_mark; oldbuf = current_buffer;
* Sanity check, in case the variable has been set to something invalid. * if (! STRINGP (Vmessages_buffer_name)) Vmessages_buffer_name = build_string (“Messages”); * Ensure the Messages buffer exists, and switch to it. If we created it, set the major-mode. * bool newbuffer = NILP (Fget_buffer (Vmessages_buffer_name)); Fset_buffer (Fget_buffer_create (Vmessages_buffer_name, Qnil)); if (newbuffer && !NILP (Ffboundp (intern (“messages-buffer-mode”)))) call0 (intern (“messages-buffer-mode”));
bset_undo_list (current_buffer, Qt); bset_cache_long_scans (current_buffer, Qnil);
oldpoint = message_dolog_marker1; set_marker_restricted_both (oldpoint, Qnil, PT, PT_BYTE); oldbegv = message_dolog_marker2; set_marker_restricted_both (oldbegv, Qnil, BEGV, BEGV_BYTE); oldzv = message_dolog_marker3; set_marker_restricted_both (oldzv, Qnil, ZV, ZV_BYTE);
if (PT = Z)
point_at_end = 1;
if (ZV =
Z)
zv_at_end = 1;
BEGV = BEG; BEGV_BYTE = BEG_BYTE; ZV = Z; ZV_BYTE = Z_BYTE; TEMP_SET_PT_BOTH (Z, Z_BYTE);
* Insert the string–maybe converting multibyte to single byte
or vice versa, so that all the text fits the buffer. *
if (multibyte
&& NILP (BVAR (current_buffer, enable_multibyte_characters)))
{
* Convert a multibyte string to single-byte
for the Message buffer. *
for (ptrdiff_t i = 0; i < nbytes; )
{
int char_bytes, c = check_char_and_length (msg + i, &char_bytes);
char work = CHAR_TO_BYTE8 (c);
insert_1_both (&work, 1, 1, true, false, false);
i = char_bytes;
}
}
else if (! multibyte
&& ! NILP (BVAR (current_buffer, enable_multibyte_characters)))
{
* Convert a single-byte string to multibyte
for the Message buffer. *
for (ptrdiff_t i = 0; i < nbytes; i+)
{
int c = make_char_multibyte (msg[i]);
unsigned char str[MAX_MULTIBYTE_LENGTH];
int char_bytes = CHAR_STRING (c, str);
insert_1_both ((char *) str, 1, char_bytes, true, false, false);
}
}
else if (nbytes)
insert_1_both (m, chars_in_text (msg, nbytes), nbytes,
true, false, false);
if (nlflag) { ptrdiff_t this_bol, this_bol_byte, prev_bol, prev_bol_byte; intmax_t dups;
* Since we call del_range_both passing false for PREPARE, we aren’t prepared to run modification hooks (we could end up calling modification hooks from another buffer and only with AFTER=t, Bug#21824). * specpdl_ref count = SPECPDL_INDEX (); specbind (Qinhibit_modification_hooks, Qt);
insert_1_both (“\n”, 1, 1, true, false, false);
scan_newline (Z, Z_BYTE, BEG, BEG_BYTE, -2, false); this_bol = PT; this_bol_byte = PT_BYTE;
* See if this line duplicates the previous one. If so, combine duplicates. * if (this_bol > BEG) { scan_newline (PT, PT_BYTE, BEG, BEG_BYTE, -2, false); prev_bol = PT; prev_bol_byte = PT_BYTE;
dups = message_log_check_duplicate (prev_bol_byte, this_bol_byte); if (dups) { del_range_both (prev_bol, prev_bol_byte, this_bol, this_bol_byte, false); if (dups > 1) { char dupstr[sizeof “ [ times]”
- INT_STRLEN_BOUND (dups)];
* If you change this format, don’t forget to also change message_log_check_duplicate. * int duplen = sprintf (dupstr, “ [%”PRIdMAX“ times]”, dups); TEMP_SET_PT_BOTH (Z - 1, Z_BYTE - 1); insert_1_both (dupstr, duplen, duplen, true, false, true); } } }
* If we have more than the desired maximum number of lines in the Messages buffer now, delete the oldest ones. This is safe because we don’t have undo in this buffer. *
if (FIXNATP (Vmessage_log_max)) { scan_newline (Z, Z_BYTE, BEG, BEG_BYTE, -XFIXNAT (Vmessage_log_max) - 1, false); del_range_both (BEG, BEG_BYTE, PT, PT_BYTE, false); }
unbind_to (count, Qnil); } BEGV = marker_position (oldbegv); BEGV_BYTE = marker_byte_position (oldbegv);
if (zv_at_end) { ZV = Z; ZV_BYTE = Z_BYTE; } else { ZV = marker_position (oldzv); ZV_BYTE = marker_byte_position (oldzv); }
if (point_at_end) TEMP_SET_PT_BOTH (Z, Z_BYTE); else * We can’t do Fgoto_char (oldpoint) because it will run some Lisp code. * TEMP_SET_PT_BOTH (marker_position (oldpoint), marker_byte_position (oldpoint));
unchain_marker (XMARKER (oldpoint)); unchain_marker (XMARKER (oldbegv)); unchain_marker (XMARKER (oldzv));
* We called insert_1_both above with its 5th argument (PREPARE) false, which prevents insert_1_both from calling prepare_to_modify_buffer, which in turns prevents us from incrementing windows_or_buffers_changed even if Messages is shown in some window. So we must manually set windows_or_buffers_changed here to make up for that. * windows_or_buffers_changed = old_windows_or_buffers_changed; bset_redisplay (current_buffer);
set_buffer_internal (oldbuf);
message_log_need_newline = !nlflag; Vdeactivate_mark = old_deactivate_mark; } }
本体コードを読む
https://systemcrafters.net/live-streams/august-20-2021/ まずビルドする。
guix environment --pure emacs-next --ad-hoc git
ほとんどはEmacs Lispで書かれている。コアな部分はC言語。
src/*
にはCのコードがある。
src/lisp.h
はEmacs Lispそのもののコードで重要。
How to read (and understand) GNU Emacs source code? : emacs
コードの読み方の参考。 ソースコードの読み方 - Solist Work Blog
プロセス管理
最初から入っている proced
が便利。一覧に加えてkillもできる。
デフォルトの動的補完 dabbrev
dabbrevはEmacsデフォルトの補完機能。大雑把で高速。 dabbrev-expand (M-/)
デフォルトの定義探す系 xref
- xref-find-definitions (M-.)
- 定義ジャンプ
- xref-find-references (M-?)
- 参照箇所(使われてるところ)にジャンプ
lspと接続しているとそっちを使って辿るようだ。
LSP設定
corfu設定
- ミニマルなパッケージを組み合わせる方法が主流である。
- 各パッケージの概要: https://tam5917.hatenablog.com/entry/2022/02/05/141115
git-timemachineで歴史を見る
emacsmirror/git-timemachine: Walk through git revisions of a file
git-timemachine
を使うと、ファイルごとで軽々とコミットを辿れて楽しい。すごいのにあまり有名ではないよう。
- n で次のコミット、p で前のコミットに移る。
- c でコミット内容を見る。ファイルビューではコミット時点のファイルだけなので、差分をみたいときにはこっちを使う。
faceを調べる
M-x list-faces-display
diredでzip展開
dired-modeで Z … dired-do-compress
で展開か圧縮できる。
矩形選択する
rectangle-mark-mode
– C-x SPC
で矩形選択できる。
back-button-modeのキーバインドが衝突してたのでback-button-modeの方を無効化。
(define-key back-button-mode-map (kbd "C-x SPC") nil)
pdf-tools
pdf-toolsはEmacs内でPDFを閲覧できるパッケージ。 インストールしても、しばらくすると利用不可になってることがあるのでinstallしなおす。 インストールするとdiredから選択するとpdf-toolsが使われる。
(pdf-tools-install)
ivy-occurで一括置換する
occurの特徴はeでedit-modeに入れる点。一覧にして複数ファイルをそのまま置換できる。
プロジェクトのファイルにすべてに対してoccurしたいとき。
counsel-git-grep
して絞り込んだものに対して (ivy-occur)
= C-c C-o
でoccurできる。
edit-modeに入るには。
(ivy-wgrep-change-to-wgrep-mode)
= C-x C-q
occurでの変更をコミットするには。
C-x C-s
。覚えにくい。
絵文字挿入
C-x 8 return
elisp-refs
Wilfred/elisp-refs: semantic code search for emacs lisp 関数や変数の参照を見つけるパッケージ。 じっさいに使われている例を探し出すことができる。
File: /gnu/store/lnwgc4ww47vkq2wv2ay3rdm0ppnmgyfy-emacs-27.2/share/emacs/27.2/lisp/format.el.gz (setq prop (pop props)) File: /gnu/store/lnwgc4ww47vkq2wv2ay3rdm0ppnmgyfy-emacs-27.2/share/emacs/27.2/lisp/window.el.gz (let ((window (pop window-state-put-stale-windows)))
キーバインドの一覧を出力するコマンド
キーバインドの一覧を出力するコマンド。
make-command-summary
。
複数行に空白挿入
複数行一気に空白を挿入してインデントを整えるとき。
C-x C-i
で挿入モードにあるのであとは方向キーで微調整できる。
文字コード変更
改行コードなどで問題が生じたとき。^Mが表示されるとか。
C-x RET f utf-8-unix
で文字コードを変更して保存する。
カウントダウンタイマー
カップ麺の3分を測りたい、みたいなとき。
(org-timer-set-timer)
company-documentation
Company is input complement package.
Read document on complementation list.
Ctrl + Shift + h
vtermのbash_profileを設定する
if [[ "$INSIDE_EMACS" = 'vterm' ]] \ && [[ -n ${EMACS_VTERM_PATH} ]] \ && [[ -f ${EMACS_VTERM_PATH}/etc/emacs-vterm-bash.sh ]]; then source ${EMACS_VTERM_PATH}/etc/emacs-vterm-bash.sh fi
パッケージのソースコードメモ
- org-lint
- interactive
- defun
projectile-toggle-between-implementation-and-test (C-c p t
)
テストとプロダクトコードを切り替える。
(magit-topic)
一覧でPRとIssueにアクセスできると行き来がしやすそう。 Forgeのトピック間移動がhelmでできたらいいのにな。
MySQLと接続してSQLを直に実行する
M-x customize-variable RET sql-connection-alist RET ... M-x sql-connect open buffer... M-x sql-mode M-x sql-set-sqli-buffer RET sql-send-buffer (C-c C-b) sqlup-capitalize-keywords-in-region (C-c u)
Macでの置換
- Macでは
query-replace
(M-%
)が奪われて押せないので、query-replace-regexp
(C-M-%
)する。
spring RSpec
Railsでspringを走らせておくとRSpecが高速に動作する。
いつのまにかEmacsでspringが動かなくなっていた。コマンドでは spring rspec
などで動かせるが、Emacsではふつうの bundle exec ...
になっていた。前は早くできていたはず。ネットの記事を参考に修正した。
emacsのrspec-modeで、上記のようにしてspringを使うと、自動ではspringがONにならない。 これはtmpにpidファイルが作られないため。 強制的にspringを使うには、次のように設定する。
(require 'rspec-mode) (defun rspec-spring-p () (and rspec-use-spring-when-possible (stringp (executable-find "spring"))))
springを自動で検出してくれるらしい(何より今までできてた)。
spring status
でspringが動いてるかチェックする。動いてなかったら spring rspec
する。
デバッグ起動する
$ emacs -q # init読み込まない $ emacs --debug-init # デバッグモードで起動する
System Crafters IRC
erc-tls irc.libera.chat 6697 /join #systemcrafters
mermaid.jsをEmacsで使う
mermaidはプレーンテキストで図を作れるツール。JavaScriptで書かれている。 これをEmacsで扱えるようにするのがmermaid-mode。
npm install -g @mermaid-js/mermaid-cli
して、C-cで画像にコンパイルできる。
各Window managerでEmacs風キーバインドにする
# gnome gsettings set org.gnome.desktop.interface gtk-key-theme Emacs # MATE gsettings set org.mate.interface gtk-key-theme Emacs # Cinnamon gsettings set org.cinnamon.desktop.interface gtk-key-theme Emacs
counsel-find-fileで新しいファイルを作る
補完選択になってしまって新しくファイルが作れないときは、C-M-jで新規作成できる。
counselのリポジトリのREADMEの最後に書かれてた。
(setq ivy-use-selectable-prompt t)
をすると上下選択できるようになる。 選択一覧にはでないのでわかりにくい。
blame系関数
vc-annotate
magit-blame
blamer
keybinding一覧
(make-command-summary)
や (describe-bindings)
で生成できる。
リンクを開くときにブラウザにフォーカスさせない
firefox側で設定する。about:config をURLバーに打ち込み、設定項目 browser.tabs.loadDivertedInBackground を true にする。 https://stackoverflow.com/questions/10506496/run-browse-url-in-emacs-without-giving-focus-to-the-browser
Tasks
TODO Evolution of Emacs Lisp
Emacs Lispの進歩。
TODO Add persist-state package by bram85 · Pull Request #8574 · melpa/melpa
Package-Versionは不要になった。
TODO textlintのパッケージを作る
今のところ、ない。
TODO マシンごとの分岐をする
- 仕事用とプライベート用の違い
- デスクトップとラップトップの違い
- モニター解像度の違い
があるので、判定できるようにする。
TODO denoteを読む
シンプルなノートパッケージ。
TODO rfc-modeを読む
rfcドキュメントを読むモード。あまり変化しない特定のドキュメントに特化したモードは便利そう。
TODO hydraはどうやって登録キーをキーマップに登録しているか DontKnow
アルファベットで指定して初期化するだけでメニュー表示に追加し、キーバインドを作成する。これはどうやっているか。hydra起動中はキーバインドが一切衝突しないのも気になる。
(defhydra hydra-zoom (global-map "<f2>") "zoom" ("g" text-scale-increase "in") ("l" text-scale-decrease "out"))
https://github.com/kd-collective/hydra/blob/2d553787aca1aceb3e6927e426200e9bb9f056f1/hydra.el#L160-L161
(when overriding-terminal-local-map (internal-pop-keymap hydra-curr-map ’overriding-terminal-local-map))))
TODO バッファ管理
rails開発で処理が多くのファイルに分散していて、同じような名前のファイルが多い場合、集中のスイッチングコストが大きくなる。今まではあまりファイル分散してなかったので counsel-switch-buffer
ひとつで問題なかったが、タスクやコードの構造によって問題になりうる。ファイル移動しようとするたびに思考が中断される。
- 方法1: projectile-railsのキーバインドを覚える https://qiita.com/yoshinarl/items/8e3c4b075a181f224591
- projectile-rails-find-current-spec(C-c r P) 対応するテストファイルを開く
- MVC系の移動 C-c r M、C-c r C、C-c r V
- 方法2: バッファをリセットする
- 無関係な似たような名前のバッファが溜まるのが問題であって、そもそもこまめにリセットすれば見やすくなる説
- 方法3: ブックマーク
- 必要なものはブックマークしておく。メンドイ。
- 方法4: タブ表示
- ブラウザのように、タブに表示する。結局ファイル名が長かったり、ファイル名が多くなるとやりにくくなるのだが、直近3つくらいのファイルが見られるだけでも改善する
良いページを発見した。Buffer management - WikEmacs
TODO ParEdit チュートリアル
すごい便利らしいのでやってみる。
TODO use-packageでパッケージ設定を書き直す
パッケージごとの設定がわかりづらい状態なので、書き直す。とくにhook、keybindあたりはあちこちに散らかっている。
TODO 今日の残り時間をゲージで表示する
時計より視覚的にわかりやすそうなので、polybarで出すようにしよう。
TODO recompileが別ウィンドウで立ち上がるようにする
recompileは直前のcompilationの再実行関数。 実行して別ウィンドウのバッファが残るはずなのだが、何かウィンドウに関するパッケージを入れたせいで挙動が変わっている。 消えてしまう。
TODO 正規表現置換が2度目以降失敗する
プログラム関係のmodeで起こることを確認(orgでは起こらない)。
vr–perform-query-replace: Match data clobbered by buffer modification hooks
TODO ソースコードの読み方 - Solist Work Blog
Emacs, Magitで解説している。
TODO .emacs.d整理
package管理もちゃんと使って設定したい。
TODO popper(emacs package)
ウィンドウの出る方法を設定できるパッケージ。
TODO EmacsとレプリカDB接続
ローカルではできるが、SSHを使う方法がわかっていない。 SQLを打つときに不便なので。
TODO lsp-mode が何かと競合する
消したときに近くの矩形が消されてしまう謎の挙動。
TODO プロジェクトファイル限定のswitch-buffer。
projectile-switch-to-buffer (C-c p b)
TODO キーバインドの一覧を表示
<F1> b
TODO プロジェクトをag検索
projectile-ag (C-c p s s
)
TODO vtermでカッコが入力できない
vtermは互換性が高いShellのターミナルパッケージ。
カッコを自動入力するモードにしていると入力できなる。 なのでカッコ関係はコピペする羽目になる。
TODO highlight-indent-guides-mode
でコピペしたときにおかしい
highlight-indent-guides-mode
でコピペしたときに表示がおかしい。
faceまでコピーするから仕方ないのかな。
TODO GTAGSを使えるようにする
読むときにたどれないと不便。
TODO 使ってないコマンドを開拓するパッケージ
最初にコマンド一覧をどこからか出力する。それから、何かコマンドを打つごとに数を保存する。そうしてしばらく使っていくと、使ったことのないコマンドが明らかになる。カバレッジ率的にやって面白そうだろう。
TODO Appendix D Tips and Conventions
Emacs Lisp Manual。いくつか面白そうな章がある。
TODO org-mode のキーバインド、その先 - Qiita
キーバインドの記事。
References
Spacious Padding (spacious-padding.el) | Protesilaos Stavrou
Emacsのpaddingをいい感じにするパッケージ。
Use GNU Emacs
新しいチュートリアル。
Org Themes collection
Org Exportテーマのコレクション。
Configurations for GNU Emacs
文芸的プログラミング。
Free software, free society: Richard Stallman at TEDxGeneva 2014 - YouTube
リチャード・ストールマンのTED Talk。
cysh
独自のサイトジェネレータで作成されたサイト。
Emacs から直接 DeepL を使う設定方法 | ハングスタック
Emacsでdeeplを使う方法。
emacs.d でないところで init.el を育てる方法
ユーザディレクトリ以外に配置した.emacs.dを使う方法。
Pragmatic Emacs | practical tips for everyday emacs
役立つtipsが紹介されているサイト。
How Emacs changed my life
Matzのスライド。 Emacsを単に道具として使うだけでなく、Rubyを書くうえでの参考にもなったという。
【Emacs入門】②バージョン28で新たにビルトインされるModus Themesを使おう
新しく追加されたthemeとその設定方法。
Avy can do anything | Karthinks
avyの解説。
EmacsWiki: Hacker Guide
Emacsの簡単な概要。
Hacking your way around in… by Marcin Borkowski
Emacsの本。買うか迷ってる。
Emacs アイコンがコミットされるまで
こんな流れなんだ。
A textlint flycheck checker in Emacs
EmacsでのTextlint設定方法。
A Simple Guide to Writing & Publishing Emacs Packages
パッケージを作ってみる記事。
alphapapa/emacs-package-dev-handbook
Emacsパッケージのハンドブック。
Making Emacs Work For Me
設定解説。
Emacs Lisp 組込み関数
基本的な関数をまとめたリスト。
(think)
Emacsのブログ。
Irreal | The minds had long ago come up with a proper name for it; they called it the Irreal, but they thought of it as Infinite Fun. That was what they really knew it as. The Land of Infinite Fun. –Iain M. Banks, Excession
Emacsのブログ。
The Kitchin Research Group
Emacsのブログ。
Theresa O’Connor
Emacsの情報集約サイト。
Emacs is Not Enough
読み物。
Archives
DONE git-linkでコミットハッシュからリンクを取る
デフォルトだと現在のブランチから名前を取るため、remoteに存在しないブランチのとき無効なリンクになる。 ブランチ名を固定もできるが、汎用性がなくなる。 コミットハッシュから取ればいい。
(setq git-link-use-commit t)
DONE esh-autosuggestを導入する
履歴を自動入力。
(use-package esh-autosuggest :hook (eshell-mode . esh-autosuggest-mode) :config (setq esh-autosuggest-delay 0.5) (set-face-foreground 'company-preview-common "#4b5668") (set-face-background 'company-preview nil))
DONE undo履歴を保持しないときがある
3つくらいしか戻れないときがあり、原因は不明。 undo-treeで戻れる。
DONE 列名を表示する
(global-display-line-numbers-mode) linum-modeよりこっちのほうがいいらしい。 linum-modeは重かった。 外観も綺麗。
DONE diredで直に編集する
(wdired-change-to-wdired-mode)
編集モードにして C-c C-c
で実行する。
DONE 見出しレベル替え
(org-cycle-level) 何も書いてない見出しでtab。
DONE 見出し挿入
C-enter 前の項目がリストでも見出しが挿入できる。
DONE C-c n
roam のプレフィクスキー。
DONE projectile-find-file
(C-c p f
)
プロジェクト全体のファイル名検索。
DONE projectile-switch-project
(C-c p p
)
プロジェクトを切り替える。 CLOSED:
DONE robe-doc
(C-c C-d
)
Rubyメソッドを調べられる。gemがあるプロジェクトのGemfileで pry
, pry-doc
をインストールして実行するとpryが起動して、以後使えるようになる。これは補完の company.el
と連携させているため、pryを起動しないことにはgemの補完は表示されない。
DONE vterm-copy-mode
→ C-c C-t
vterm上で、eshellなどのように自由に動き回るモード。
DONE 使用パッケージで分岐するとき、どうやってbyte-compileエラーを回避するのか
たとえばhelmを使ってるときはこれ、ivyのときはこれとかでrequireするものは変わるものだが。既存パッケージはどうしているのだろう。
↓とかやった。外部のコマンドは最初に定義しておいた。
(defvar w3m-current-url) (declare-function w3m-current-title "ext:w3m-util")
DONE roamリポジトリをサイト公開する
どうにかして静的ページとして公開できるはず。 かっこいいのがなければ作る。
いい感じにやっているサイトはいくつもある。
- http://juanjose.garciaripoll.com/blog/org-mode-html-templates/index.html
- https://diego.codes/post/blogging-with-org/
- https://hugocisneros.com/org-config/#configuration
- https://hugocisneros.com/blog/my-org-roam-notes-workflow/
- https://doubleloop.net/2020/08/21/how-publish-org-roam-wiki-org-publish/
- https://notes.alexkehayias.com/org-roam/
- https://www.mtsolitary.com/20210318221148-emacs-configuration/#hugo-support
DONE org-publishのスタイルを設定する
https://ogbe.net/blog/blogging_with_org.html
contentのhtmlをいじることができなかったが、とりあえずcssで指定してOK。
DONE autosaveが出てきてうざい
Guixでは編集すると毎回プロンプトが出てくる。
(defun ask-user-about-supersession-threat (fn) "blatantly ignore files that changed on disk" ) (defun ask-user-about-lock (file opponent) "always grab lock" t) ;; or (setq revert-without-query '(".*"))
を実行したが、変わらなかった。
↓できた。
(setq auto-save-timeout 2) (setq auto-save-visited-interval 2) (setq auto-save-no-message t) (auto-save-visited-mode)
CLOSE Projectileの幅がせまくて見づらい
ほかのcompletionは幅いっぱいにハイライトされるが、projectileは文字のあるところしかハイライトされないので短い検索のときに見えにくい。たとえば counsel-find-file
とか counsel-find-file
と比べるとわかる。
counsel-projectileを使えば問題ない。でもデフォルトの動作がおかしいので修正したいところ。 いや、ivyの問題ぽい。ivy yasnippetをすると同じような状態になる。テーマを変えてもそうなる。
org-refileでも同じような感じ。要調査。 共通点は、右側にアノテーションが出ないときか。そのときはfaceが設定されないので右側まで伸びない。
DONE org-mode のキーバインド
見出し移動とか。
DONE lsp setup(TypeScript)
- install language-server
https://deno.land/#installation
curl -fsSL https://deno.land/x/install/install.sh | sh
- Install lsp-mode package
- Add lsp settings to init.el
- emacsにlsp-mode入れた - takeokunn’s blog
DONE Vterm settings
- prompt settings
DONE lispxmpを設定する
xmp-filterのelisp版。
(require 'lispxmp) (define-key emacs-lisp-mode-map (kbd "C-c C-d") 'lispxmp)
DONE pareditを設定する
;;;括弧の対応を保持して編集する設定 (require 'paredit) (add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) (add-hook 'lisp-interaction-mode-hook 'enable-paredit-mode) (add-hook 'lisp-mode-hook 'enable-paredit-mode) (add-hook 'ielm-mode-hook 'enable-paredit-mode)
DONE リンクを保存する方法をどうにかする
ブラウザからコピペするのがメンドイのでどうにかする。 org-protocolを設定して簡単に保存できるようにした。
DONE 踏み台SSH接続を簡単にできるように
Emacsはあまり関係ない。 pecoを設定して簡単に接続できるようにした。 Ubuntuにpecoを導入する(for bash) (for fish) - Qiita
alias sshp='ssh $(grep Host ~/.ssh/config | grep -v HostName | cut -d" " -f2 | peco)'
DONE vterm-toggleで初回崩れるのを直す
READMEに書かれている設定を書いたら直った。前もコピペしてたはずなので、修正されたのだろう。
DONE hydra設定
すぐ自分で設定したキーバインドを忘れる対策で、hydraを設定した。
DONE leaf.elはどうやって動いているか DontKnow
基本の動きは把握した。
キーワードを指定することで、インストール、フック、キーボードバインドまで、パッケージ周りの設定をうまくやってくれる。これはどうやっているのだろうか。
- キーワードをマクロで変換して、処理を実行してるぽい。たとえばpackageキーワードの場合、最終的にpackage.elのpackage-installが呼ばれてインストールする、というように。leaf自体が処理を持っているわけではない
- leaf関数がメイン。各キーワードをapplyして、対応する関数を実行している
- leaf keywordで定義されているキーワードの処理の中身はhandler系
- defmacroが大量に使われている。マクロを理解していないと読めない
- とほほのLISP入門 - とほほのWWW入門でdefmacroをおさらいする
(defmacro double (x) (list '* 2 x)) (double 3)
6
(defmacro double (x) (list '+ x x)) (defvar a1 2) (double (incf a1)) ;; (+ (incf a1) (incf a1)) ;; (+ 3 4)
7
(defmacro double (x) (list '+ x x)) (defvar a1 2) (macroexpand '(double (incf a1)))
(+ (incf a1) (incf a1))
バッククォートでマクロを表現。コンマで部分評価する。
(defmacro add(x y) (list '+ x y)) ; `を使わないと、若干面倒 (defmacro add(x y) `(+ ,x ,y)) ;; x, yを評価 (add 1 2)
3
リスト展開。
(defmacro if-do (test &body do-list) `(if ,test (progn ,@do-list))) (if-do (> 5 3) (print "AAA") (print "BBB"))
DONE Mastering Emacs
Emacsの本。
メモ。
- paragraph-start 巨大な正規表現…
- リージョンの選択領域が出るのはtransient markモードを使用しているから。これがデフォルトになる前は記憶していた…
DONE effortをpolybarに表示する
- [0:54/2:00] の時間を表示
- 目標時間は org-clock-effort
- effortが設定されてないときは設定してない表示をする
- 開始してないときは何も表示しない
DONE 最後に開いていたworkspaceコマンドを開く関数を追加する
マルチディスプレイを使うときに不便なので、トグル形式で切り替えられるようにする。
DONE 設定をエクスポートして文芸的にする
自分だけでなく、他人も理解できるようにする。
CLOSE vtermでコピーしたときにキルリングに入らない
一度読み取り専用バッファにして、コピーしないといけない。
DONE 設定整理
- 使ってないパッケージの整理
- サジェストまわりの設定
Backlinks
- Org-roam
- Ruby
- Rails
- Emacs Lisp
- TextLint
- create-link
- current-word-highlight
- Archive
- Docker
- History
- ej-dict
- Rust
- Git
- workflow
- EXWM
- org-mode
- C言語
- Go
- LISP
- dotfiles
- System Crafters
- magit-forge
- magit
- projectile
- Wikipedia
- career
- GNU
- 100knocks
- GraphQL
- gemat
- chore
- KDOC 1: denoteを使う
- KDOC 15: Emacs Caskを読む
- KDOC 16: 2022年のまとめ
- Mermaid
- KDOC 98: org-roam-node-findでエントリが出なくなったときの直し方
- KDOC 104: やりたいことが多すぎる
- KDOC 116: コードへの過大評価
- KDOC 118: ハックできる認知範囲を増やす
- KDOC 146: org-roam-uiをデプロイした
- Insomnia