;;; init.el --- Init file for my own -*- lexical-binding: t; -*-
;; Author: derui <[email protected]>
;; Maintainer: derui <[email protected]>
;;; Commentary:
;;: Customization:
;;; Code:
;; DO NOT EDIT THIS FILE DIRECTLY
https://zenn.dev/takeokunn/articles/56010618502ccc
(defconst my:before-load-init-time (current-time))
;;;###autoload
(defun my:load-init-time ()
"Loading time of user init files including time for `after-init-hook'."
(let ((time1 (float-time
(time-subtract after-init-time my:before-load-init-time)))
(time2 (float-time
(time-subtract (current-time) my:before-load-init-time))))
(message (concat "Loading init files: %.0f [msec], "
"of which %.f [msec] for `after-init-hook'.")
(* 1000 time1) (* 1000 (- time2 time1)))))
(add-hook 'after-init-hook #'my:load-init-time t)
(defun my:emacs-init-time ()
"Emacs booting time in msec."
(interactive)
(message "Emacs booting time: %.0f [msec] = `emacs-init-time'."
(* 1000
(float-time (time-subtract
after-init-time
before-init-time)))))
(add-hook 'after-init-hook #'my:emacs-init-time)
(defvar my:setup-tracker--level 0)
(defvar my:setup-tracker--parents nil)
(defvar my:setup-tracker--times nil)
(defvar my:setup-tracker-enabled nil)
(when my:setup-tracker-enabled
(when load-file-name
(push load-file-name my:setup-tracker--parents)
(push (current-time) my:setup-tracker--times)
(setq my:setup-tracker--level (1+ my:setup-tracker--level)))
(add-variable-watcher
'load-file-name
(lambda (_ v &rest __)
(cond ((equal v (car my:setup-tracker--parents))
nil)
((equal v (cadr my:setup-tracker--parents))
(setq my:setup-tracker--level (1- my:setup-tracker--level))
(let* ((now (current-time))
(start (pop my:setup-tracker--times))
(elapsed (+ (* (- (nth 1 now) (nth 1 start)) 1000)
(/ (- (nth 2 now) (nth 2 start)) 1000))))
(with-current-buffer (get-buffer-create "*my:setup-tracker*")
(save-excursion
(goto-char (point-min))
(dotimes (_ my:setup-tracker--level) (insert "> "))
(insert
(file-name-nondirectory (pop my:setup-tracker--parents))
" (" (number-to-string elapsed) " msec)\n")))))
(t
(push v my:setup-tracker--parents)
(push (current-time) my:setup-tracker--times)
(setq my:setup-tracker--level (1+ my:setup-tracker--level)))))))
(eval-when-compile
(defvar elpaca-installer-version 0.7)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil :depth 1
:files (:defaults "elpaca-test.el" (:exclude "extensions"))
:build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(when (< emacs-major-version 28) (require 'subr-x))
(condition-case-unless-debug err
(if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (apply #'call-process `("git" nil ,buffer t "clone"
,@(when-let ((depth (plist-get order :depth)))
(list (format "--depth=%d" depth) "--no-single-branch"))
,(plist-get order :repo) ,repo))))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(progn (message "%s" (buffer-string)) (kill-buffer buffer))
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
;; 全体を更新するために先頭に追加する
(add-hook 'emacs-startup-hook #'elpaca-process-queues))
exec-pathに必要なパスを追加する。
(add-to-list 'exec-path (expand-file-name "~/.npm/bin"))
(add-to-list 'exec-path (expand-file-name "~/.asdf/shims"))
(add-to-list 'exec-path "/usr/local/bin")
(add-to-list 'exec-path "/usr/bin")
(add-to-list 'exec-path "/usr/sbin")
(add-to-list 'exec-path my:user-local-exec-path)
(add-to-list 'exec-path (expand-file-name "bin" my:roswell-path))
(add-to-list 'exec-path (expand-file-name "bin" my:cargo-path))
git管理外になっているファイル。これは、パス関連など、環境毎に違うので登録するとめんどくさいものに対して利用する。
(let ((user-env (locate-user-emacs-file "conf/user-env.el")))
(load user-env t))
設定で使う簡単なmacro を定義する。
(defmacro darwin-cli! (&rest body)
"macOSかつCLIでの起動時にだけ有効になる設定を書くMacro"
(if (and (eq system-type 'darwin) (not window-system))
`(progn ,@body)
nil))
(defmacro darwin-gui! (&rest body)
"macOSかつGUIでの起動時にだけ有効になる設定をするmacro"
(if (and (eq system-type 'darwin) window-system)
`(progn ,@body)
nil))
(defmacro darwin! (&rest body)
"macOSでの起動時にだけ有効になる設定をするmacro"
(if (eq system-type 'darwin)
`(progn ,@body)
nil))
(defmacro linux! (&rest body)
"Linux環境での起動次にだけ有効になる設定をするmacro"
(if (and (eq system-type 'gnu/linux))
`(progn ,@body)
nil))
(defmacro each! (lst &rest body)
"`lst'をloop-unrollingしたものに転換する。
リストの要素は `it' でアクセスできる"
(declare (indent 1))
`(progn
,@(mapcar (lambda (v)
`(let ((it (quote ,v)))
,@body)
)
lst)))
(defmacro load-package (symbol)
"`symbol' に対応するload-pathを追加する"
(declare (indent 1))
(let* ((dir (expand-file-name user-emacs-directory))
(package-name (cond ((symbolp symbol)
(symbol-name symbol))
(t symbol)))
(autoload-name (seq-concatenate 'string package-name "-autoloads"))
(autoload-file-name (file-name-concat dir "elpaca" "builds" package-name
(seq-concatenate 'string autoload-name ".el") ))
(locate-autoload-p (file-exists-p autoload-file-name)))
`(progn
(message "Loading %s/%s..." ,package-name ,autoload-name)
(add-to-list 'load-path ,(file-name-concat dir "elpaca" "builds" package-name))
,(when locate-autoload-p
`(load ,autoload-file-name nil t t t)))
))
遅延して実行するための簡単なQueue機構。
(defvar my:high-priority-startup-queue nil
"高いPriorityで実行されるQueue")
(defvar my:high-priority-startup-timer nil)
(defvar my:low-priority-startup-queue nil
"低いPriorityで実行されるQueue")
(defvar my:low-priority-startup-timer nil)
(defmacro with-high-priority-startup (&rest body)
"high priorityな遅延処理を登録する"
(declare (indent 0))
`(setq my:high-priority-startup-queue
(append my:high-priority-startup-queue ',body)))
(defmacro with-low-priority-startup (&rest body)
"low priorityな遅延処理を登録する"
(declare (indent 0))
`(setq my:low-priority-startup-queue
(append my:low-priority-startup-queue ',body)))
(add-hook 'emacs-startup-hook
;; Timerを開始して、 `my:high-priority-startup-queue' にある処理を実行していく
(lambda ()
(setq my:high-priority-startup-timer
(run-with-timer 0.01 0.001
(lambda ()
(if my:high-priority-startup-queue
(let ((inhibit-message t))
(eval (pop my:high-priority-startup-queue)))
(cancel-timer my:high-priority-startup-timer)))))
;; Timerを開始して、 `my:low-priority-startup-queue' にある処理を実行していく
(setq my:low-priority-startup-timer
(run-with-timer 0.03 0.001
(lambda ()
(if my:low-priority-startup-queue
(let ((inhibit-message t))
(eval (pop my:low-priority-startup-queue)))
(cancel-timer my:low-priority-startup-timer)))))))
(darwin!
;; altとMetaを入れ替える
(setq mac-option-modifier 'alt)
(setq mac-command-modifier 'meta))
(darwin-gui!
;; macOSで描画がかなり遅いのを解消できるかもしれない設定
(add-to-list 'default-frame-alist '(inhibit-double-buffering . t)))
(setq font-lock-support-mode 'jit-lock-mode)
(defface my-face-b-2 '((t (:background "gray26"))) "face for tab" :group 'my)
(defface my-face-u-1 '((t (:foreground "SteelBlue" :underline t))) "" :group 'my)
(defvar my-face-b-2 'my-face-b-2)
(defvar my-face-u-1 'my-face-u-1)
(defun my:font-lock-mode (&rest _)
(font-lock-add-keywords
major-mode
'(("\t" 0 my-face-b-2 append)
("[ \t]+$" 0 my-face-u-1 append))))
(advice-add 'font-lock-mode :before 'my:font-lock-mode)
Emacs 28.1からデフォルト値が変更されたので、元々のfaceに合うように戻す。
(with-eval-after-load 'bookmark
(set-face-attribute 'bookmark-face nil :foreground 'unspecified :background 'unspecified :inherit 'unspecified))
Emacs 29でなんでかmode lineのフォントとしてvariable pitchが利用されるような設定が追加されたので、同じものを利用するようにする。
mode lineをvariable pitchではなく等幅フォントを利用する。
(set-face-attribute 'mode-line-active nil :inherit 'mode-line)
(with-high-priority-startup
(global-font-lock-mode +1)
(show-paren-mode +1)
(transient-mark-mode +1)
;; pixelベースのスクロール処理
(pixel-scroll-precision-mode +1)
)
https://blog.tomoya.dev/posts/how-to-automatically-switch-lsp-servers-in-lsp-mode/
denoとts-lsを切り替えながらやりたい場合などに利用する。directory localを適用してからhookを実行したい場合は、 <major-mode>-local-vars-hook
というhookを実行すること。
(defun my:run-local-vars-mode-hook ()
"Run `major-mode' hook after the local variables have been processed."
(run-hooks (intern (concat (symbol-name major-mode) "-local-vars-hook"))))
(add-hook 'hack-local-variables-hook 'my:run-local-vars-mode-hook)
(defun my:save-all-buffers ()
"focusの状態が変ったら、全bufferを保存する"
(when (not (frame-focus-state))
(save-some-buffers "!")))
(add-function :after after-focus-change-function #'my:save-all-buffers)
(setopt sentence-end-double-space nil)
(with-eval-after-load 'browse-url
(cond
((executable-find "firefox")
(progn
(setq browse-url-browser-function #'browse-url-firefox)
(setq browse-url-generic-program "firefox")
(setq browse-url-firefox-program "firefox")))
((executable-find "chromium")
(progn
(setq browse-url-browser-function #'browse-url-chromium)
(setq browse-url-generic-program "chromium")))
((executable-find "vivaldi")
(progn
(setq browse-url-browser-function #'browse-url-chromium)
(setq browse-url-generic-program "vivaldi")))))
(with-low-priority-startup
(defun my:copy-input-and-exit ()
"Copy the current input to the kill ring and exit."
(interactive)
(let ((buffer (current-buffer))
(select-enable-clipboard t))
(my:copy-with-system-clipboard (buffer-substring-no-properties (point-min) (point-max)))
(or (delete-frame)
(server-edit))
(with-current-buffer buffer
(let (kill-buffer-hook kill-buffer-query-functions)
(set-buffer-modified-p 'nil)
(kill-buffer)))))
(define-minor-mode temporary-edit-mode
"Temporary editing mode with server"
:keymap (let ((map (make-sparse-keymap)))
(keymap-set map "C-c C-y" 'my:copy-input-and-exit)
map))
(add-hook 'server-switch-hook #'temporary-edit-mode))
(with-low-priority-startup
(server-start))
(with-eval-after-load 'server
;; serverで開いたバッファをkillする
(setopt server-kill-new-buffers t)
;; COMMIT_EDITMSGも一時ファイルとして扱う
(setopt server-temp-file-regexp "\\`/tmp/Re\\|/draft\\|COMMIT_EDITMSG\\'")
)
(declare-function 'my:dired-do-native-comp nil)
(declare-function 'my:dired-next-buffer-on-window nil)
(declare-function 'my:dired-balance nil)
(declare-function 'my:window-transient nil)
(declare-function 'dired-up-directory nil)
(declare-function 'dired-find-file nil)
(declare-function 'dired-next-line nil)
(declare-function 'dired-previous-line nil)
(with-eval-after-load 'dired
(keymap-set dired-mode-map "N" #'my:dired-do-native-comp)
;; dired内でもhjklで移動できるようにしておく
(keymap-set dired-mode-map "h" #'dired-up-directory)
(keymap-set dired-mode-map "l" #'dired-find-file)
(keymap-set dired-mode-map "j" #'dired-next-line)
(keymap-set dired-mode-map "k" #'dired-previous-line)
(keymap-set dired-mode-map "<left>" #'dired-up-directory)
(keymap-set dired-mode-map "<right>" #'dired-find-file)
(keymap-set dired-mode-map "<down>" #'dired-next-line)
(keymap-set dired-mode-map "<up>" #'dired-previous-line)
;; 2画面ファイラっぽく、次に開いているdiredバッファに移動できるようにする
(keymap-set dired-mode-map "<tab>" #'my:dired-next-buffer-on-window)
(keymap-set dired-mode-map "." #'my:dired-balance)
(keymap-set dired-mode-map "C-w" #'my:window-transient)
;; / でisearchできるようにする
(keymap-set dired-mode-map "/" #'isearch-forward)
;; r でwdired modeに変更する
(keymap-set dired-mode-map "r" #'wdired-change-to-wdired-mode)
;; configurations
;; diredでファイルをコピーする際に、コピー先をもう一つのdiredに切り替える
(setopt dired-dwim-target t)
(setopt dired-recursive-copies 'always)
(setopt dired-recursive-deletes 'always)
(setopt dired-listing-switches "-al --group-directories-first")
;; 標準で用意された、新規にdiredを開かないようにするための処理
(setopt dired-kill-when-opening-new-dired-buffer t)
(darwin!
;; macOSの場合、lsがcoreutilsとは別物なので、coreutils版の方を利用するように切り替える
(setopt insert-directory-program "gls"))
)
(with-low-priority-startup
;; wdired-modeに入った時点でmultistate modeにする
(declare-function multistate-mode 'multistate)
(add-hook 'wdired-mode-hook #'multistate-mode)
(defun my:dired-do-native-comp ()
"選択されているファイルをnative-compする"
(interactive)
(when-let* ((file (dired-get-filename))
(enabled (fboundp 'native-compile-async)))
(condition-case err
(native-compile-async file)
(error (dired-log "native-compile error for %s:\n%s\n" file err)))))
(defun my:dired-next-buffer-on-window ()
"現在のdiredバッファ以外で、かつ他のwindowに存在しているdired bufferに移動する。
対象になるバッファが無い場合は何もしない"
(interactive)
(when-let ((next-dired-buffer (seq-find
(lambda (buf)
(and (eq 'dired-mode (buffer-local-value 'major-mode buf))
(not (eq (current-buffer) buf))
(get-buffer-window buf)))
(buffer-list))))
(select-window (get-buffer-window next-dired-buffer))))
(defun my:dired-balance ()
"diredを使うにあたってよく利用する状態になるように調整する.
- 今のdired bufferが side-window用の場合は何もしない
- windowが一つしかない場合、vertical splitをする
- windowが3つ以上ある場合、2つにする
- windowが2つあるが、片方がdired bufferではない場合、current bufferを表示する
"
(interactive)
(unless (window-parameter (selected-window) 'window-side)
(when (< 2 (count-windows))
(delete-other-windows))
(when (= 1 (count-windows))
(split-window-horizontally))
(let* ((current-w (get-buffer-window (current-buffer)))
(b (seq-find (lambda (buf)
(let ((w2 (get-buffer-window buf)))
;; side windowは対象にしない
(and (not (equal current-w w2))
(not (window-parameter w2 'window-side)))
))
(buffer-list)))
(w (get-buffer-window b))
(other-buffer-mode (buffer-local-value 'major-mode b)))
(when (and (not (eq 'dired-mode other-buffer-mode))
w)
(save-current-buffer
(select-window w)
(switch-to-buffer (current-buffer))
)
))))
)
バッファ名を単一化するためのpackage。
(with-eval-after-load 'uniquify
(setopt uniquify-buffer-name-style 'forward)
(setopt uniquify-separator "/")
(setopt uniquify-after-kill-buffer-p t) ; rename after killing uniquified
(setopt uniquify-ignore-buffers-re "^\\*") ; don't muck with special buffers
)
(with-low-priority-startup
(require 'uniquify))
(with-eval-after-load 'shell
(setopt explicit-shell-file-name "/bin/bash")
(setopt shell-file-name "/bin/bash")
(setq shell-command-switch "-c"))
;; Emacsからの起動であることを明示する
(setenv "EMACS" "t")
(with-eval-after-load 'recentf
;; 最大1000まで保存するようにする
(setopt recentf-max-saved-items 1000)
;; /tmpのものはそもそも残らないようにする
(add-to-list 'recentf-exclude "/tmp/*"))
(with-low-priority-startup
(require 'recentf)
(recentf-mode +1))
(with-low-priority-startup
(keymap-global-set "M-/" #'dabbrev-completion)
(keymap-global-set "C-M-/" #'dabbrevv-expand)
)
プロジェクト管理用の各種基本的な処理を提供してくれる。projectileより機能としては少ないが、必要十分な機能はある。
(with-eval-after-load 'project
(defun my:project-try-nodejs (dir)
"Find a super-directory of DIR containing a package.json file."
(let ((dir (locate-dominating-file dir "package.json")))
(and dir (cons 'explicit dir))))
(cl-defmethod project-root ((project (head explicit)))
(cdr project))
(add-hook 'project-find-functions #'my:project-try-nodejs)
)
(with-eval-after-load 'files
(setopt auto-save-default t)
;; visited-modeでは5秒ごとに変更する
(setopt auto-save-visited-interval 5)
;; 5秒操作がなかったら自動保存
(setopt auto-save-interval 5))
(with-low-priority-startup
(require 'files)
(auto-save-mode +1)
;; 保存するfileはbufferと同じ名前にする。globalなminor mode
(auto-save-visited-mode +1)
)
標準であるpairの挿入package。
(with-low-priority-startup
(add-hook 'prog-mode-hook #'electric-pair-local-mode))
consult/isearchを使い分けたいので、設定する。
(with-eval-after-load 'isearch
;; isearchでwrapするときにdingを鳴らさない
(setopt isearch-wrap-pause t)
;; 検索する方向を変えるときに、再度検索し直す
(setopt isearch-repeat-on-direction-change t)
;; isearchを実行しているときにlazinessに件数をカウントする
(setopt isearch-lazy-count nil)
(setopt lazy-count-prefix-format "(%s/%s) ")
(setopt lazy-count-suffix-format nil)
;; highlightをlazyにする
(setopt isearch-lazy-highlight t)
(setopt lazy-highlight-no-delay-length 4)
(each! (;; abortだと戻ってしまうため、cancel にしている
("C-g" isearch-cancel)
;; C-hで文字の削除
("C-h" isearch-delete-char)
;; C-oでTransientを起動する
("C-o" my:isearch-transient)
;; 上下の矢印キーで履歴を上下できるようにする
("<up>" isearch-ring-reteat)
("<down>" isearch-ring-advance)
;; 左右の矢印キーで前後に移動できるようにする。
("<right>" isearch-repeat-forward)
("<left>" isearch-repeat-backward)
)
(keymap-set isearch-mode-map (car it) (cadr it))))
(with-eval-after-load 'auto-revert
;; revertの間隔は5秒としておく
(setopt auto-revert-interval 5)
)
(with-low-priority-startup
(global-auto-revert-mode +1))
コマンドの実行履歴を保存する。
(with-low-priority-startup
(load-package savehist)
(savehist-mode +1))
(seq-do (lambda (spec)
(keymap-global-set (car spec) (cadr spec)))
'(
;; C-zはmultistate modeで使うのでnilにしておく
("C-z" nil)
;; C-hは全体的にbackspaceとして扱う
("C-h" backward-delete-char)
;; C-hの内容は一応こっちに移す
("M-?" help-for-help)
;; C-mはenterとして扱う
("C-m" newline-and-indent)
("C-x /" dabbrev-expand)
("C-x ," delete-region)
("M-;" comment-dwim)
("C-x C-b" ibuffer)
("C-/" undo)
;; yank-popはconsultを利用する
("M-y" consult-yank-pop)
("C-<tab>" completion-at-point)
("M-i" backward-paragraph)
("M-o" forward-paragraph)
("C-;" consult-buffer)
;; F2はflymakeの移動で使うのでdefaultの挙動は廃止しておく
("<f2>" nil)
)
)
(keymap-set read-expression-map "TAB" #'completion-at-point)
mouseの価値を見直すことにしたので、きちんと設定することにする。
;; スクロールしているときはpointを移動しないようにする
(setopt scroll-preserve-screen-position t)
;; マウスでdragした領域はcopyする。
(setopt mouse-drag-copy-region t)
;; focusしたときのtooltipなどを削除する
(setopt mouse-highlight nil)
;; 右クリックはcontext menuなので、一回切る
(keymap-global-unset "C-<down-mouse-3>")
;; Shift + clickを使いたいので、menu表示で使われているこのキーは廃止しておく。
(keymap-global-unset "S-<down-mouse-1>")
;; 右クリックで定義に飛ぶようにするが、このままだとmenuを出すのに吸われてしまうので、unsetしておく
(keymap-global-unset "C-<down-mouse-1>")
;; mouseだけで定義に飛んだり戻ったりできるようにする
(keymap-global-set "C-<mouse-1>" #'xref-find-definitions)
(keymap-global-set "C-<mouse-3>" #'xref-go-back)
(with-low-priority-startup
(defun my:no-kill-new-duplicate (yank &optional _)
"kill-ringにおなじ内容が保存されないようにする"
(setq kill-ring (delete yank kill-ring)))
(advice-add 'kill-new :before #'my:no-kill-new-duplicate)
(defun my:no-kill-empty-only-content (f &rest args)
"空文字列に相当する場合はkill-ringに保存しないようにする"
(let* ((yank (car args)))
(unless (string-blank-p yank)
(apply f args))))
(advice-add 'kill-new :around #'my:no-kill-empty-only-content))
(linux!
(when (eq window-system 'x)
(setopt select-enable-clipboard t)
(setopt select-enable-primary nil))
(when (eq window-system 'pgtk)
(defvar my:wl-copy-process nil)
(defun my:wl-copy (text)
(setq my:wl-copy-process (make-process :name "wl-copy"
:buffer nil
:command '("wl-copy" "-f" "-n")
:connection-type 'pipe
:noquery t))
(process-send-string my:wl-copy-process text)
(process-send-eof my:wl-copy-process))
(defun my:wl-paste ()
(if (and my:wl-copy-process (process-live-p my:wl-copy-process))
nil ; should return nil if we're the current paste owner
(shell-command-to-string "wl-paste -n | tr -d \r")))
(declare-function my:wl-copy "init.el")
(declare-function my:wl-paste "init.el")
(setq interprogram-cut-function #'my:wl-copy)
(setq interprogram-paste-function #'my:wl-paste)))
(defun my:buffer-name-list ()
"Get list of buffer name"
(mapcar (function buffer-name) (buffer-list)))
(defun my:delete-trailing-whitespace ()
"delete trailing whitespace if the buffer is associated
a major mode in `my:trailing-whitespace-exclude-modes'"
(unless (seq-some (lambda (x) (eq major-mode x)) my:trailing-whitespace-exclude-modes)
(delete-trailing-whitespace)))
(defun my:minor-mode-active-p (mode)
"return specified minor mode is active or not"
(let ((active-modes (cl-remove-if-not (lambda (it) (and (boundp it) (symbol-value it))) minor-mode-list)))
(member mode active-modes)))
(defun my:copy-with-system-clipboard (str)
"Copy passed string to system clipboard.
This function does not add `str' to the kill ring."
(when (display-graphic-p)
(cond
((eq system-type 'darwin)
(let ((proc (make-process :name "pbcopy" :buffer nil :command '("pbcopy") :connection-type 'pipe)))
(process-send-string proc str)
(process-send-eof proc)
(kill-process proc)))
((and (eq system-type 'gnu/linux)
(eq window-system 'pgtk)
(executable-find "wl-copy"))
(let ((proc (make-process :name "wl-copy"
:buffer nil
:command '("wl-copy" "-f" "-n")
:connection-type 'pipe)))
(process-send-string proc str)
(process-send-eof proc)
(kill-process proc)))
(t
nil)
)))
https://github.com/magnars/expand-region.el/pull/279/files
上記のPrを参考に。
(with-low-priority-startup
(defun my:treesit-expand-region--between-node (a b)
"`(A B)' の間に存在するnodeを取得する"
(let ((start (min a b))
(end (max a b)))
(treesit-parent-until
(treesit-node-at start)
(lambda (node) (< end (treesit-node-end node)))))
)
(defun my:treesit-expand-region--parent-node ()
"pointの位置にあるnodeの親を取得する"
(when-let* ((node (if (region-active-p)
(my:treesit-expand-region--between-node (region-beginning) (region-end))
(treesit-node-at (point)))))
(goto-char (treesit-node-start node))
(set-mark (treesit-node-end node))
(activate-mark))
)
(defun my:treesit-expand-region ()
"treesitが有効な場合にexpand regionを実施する。treesitが有効ではない場合はpuniを利用する"
(interactive)
(if (and (functionp 'treesit-available-p)
(treesit-available-p)
(treesit-language-at (point))
)
(my:treesit-expand-region--parent-node)
(puni-expand-region))
))
(with-low-priority-startup
(defun my:kill-word-or-kill-region (f &rest args)
"kill-regionにおいて、リージョンが選択されていない場合にはbackward-kill-wardを実行するように。"
(if (and (called-interactively-p 'interactive) transient-mark-mode (not mark-active))
(backward-kill-word 1)
(apply f args)))
(advice-add 'kill-region :around 'my:kill-word-or-kill-region))
(with-low-priority-startup
(defun my:kill-line-and-fixup (f &rest args)
"kill-lineの際に、次の行の行頭に連続している空白を削除する"
(if (and (not (bolp)) (eolp))
(progn
(forward-char)
(fixup-whitespace)
(backward-char))
(apply f args)))
(advice-add 'kill-line :around 'my:kill-line-and-fixup))
なぜかこの処理が存在しなかったので追加する。
(with-low-priority-startup
(defun my:upcase-char ()
"upcase current point character"
(interactive)
(save-excursion
(let* ((current-point (point))
(upcased (s-upcase (buffer-substring-no-properties current-point (1+ current-point)))))
(replace-region-contents current-point (1+ current-point) (lambda () upcased)))))
(defun my:downcase-char ()
"downcase current point character"
(interactive)
(save-excursion
(let* ((current-point (point))
(downcased (s-downcase (buffer-substring-no-properties current-point (1+ current-point)))))
(replace-region-contents current-point (1+ current-point) (lambda () downcased))))))
scratchバッファはlockしておいて、対応できるようにする。
(with-low-priority-startup
(with-current-buffer "*scratch*"
(emacs-lock-mode 'kill)))
hide-mode-lineの超簡易版。対象のbufferでだけmode lineを削除する。削除したものは復元できないものとする。
(defun my:hide-mode-line ()
"hide mode line on current buffer"
(setq mode-line-format nil))
https://www.gnu.org/software/emacs/manual/html_node/elisp/Side-Windows.html
Side windowという形で、frameの特定の側にwindowを作成することができる。
(defvar my:display-buffer-list-in-side-window nil)
(setq my:display-buffer-list-in-side-window
`(((0 left) . ,(rx (or
"*completion*"
"*Help*"
"*Messages*"
;; magit-staus系統はside window
"magit: "
)))
((0 bottom) . ,(rx (or
;; deepl系統もside window
"*DeepL Translate*"
"*vterm*"
(regexp "[wW]arnings\\*$")
(regexp "[oO]utput\\*$")
(regexp "^\\*Flymake diagnostics"))))
((1 right) . ,(rx (or
;; xref-referenceとかで分割されるのが結構ストレスなので
"*xref*"
)))
((0 right) . ,(rx (or
;; eldocのbuffer
(regexp "^\\*eldoc.*\\*$")
)))
((1 left) . ,(rx (or
;; commit messageはmagitと並ぶ格好にする
"COMMIT_EDITMSG")))))
(with-low-priority-startup
(setq display-buffer-alist nil)
(seq-do (lambda (x)
(let* ((config-slot (caar x))
(config-side (cadar x))
(config-buffer-regexp (cdr x)))
(add-to-list 'display-buffer-alist
`(,config-buffer-regexp
(display-buffer-in-side-window)
(side . ,config-side)
(slot . ,config-slot)
(dedicated . t)
(window-width . 0.25)
(window-parameters . ((no-other-window . nil) ; disable because it makes me easier to switch window
(no-delete-other-windows . t)))))
))
my:display-buffer-list-in-side-window))
deeplと連携して、翻訳した文章をコピペするための処理を提供する。
(defcustom my:deepl-auth-key nil
"Auth key for deepl"
:group 'my
:type '(string))
(defcustom my:deepl-api-host "api-free.deepl.com"
"The host for deepl API. Use `api-free' when your plan is free."
:type 'string
:group 'my)
(defcustom my:deepl-send-confirmation-threshold 3000
"Threshold of string before sending deepl"
:type 'string
:group 'my)
(eval-when-compile
(elpaca request))
(with-low-priority-startup
(load-package request))
(cl-defun my:deepl-send-string-confirm (&key _)
"Do confirmation before sending large string to deepl."
(y-or-n-p
(format "It's over %d characters, do you really want to send it" my:deepl-send-confirmation-threshold)))
(cl-defun my:deepl-translate-internal (text source-lang target-lang callback)
"Call deepl translate with confirmation."
(when (and (> (length text) my:deepl-send-confirmation-threshold)
(not (my:deepl-send-string-confirm)))
(cl-return-from my:deel-translate-internal))
(request (format "https://%s/v2/translate" my:deepl-api-host)
:method "POST"
:data `(
("auth_key" . ,my:deepl-auth-key)
("text" . ,text)
("source_lang" . ,source-lang)
("target_lang" . ,target-lang))
:parser 'json-read
:success callback))
(cl-defun my:deepl-output-message (&key data &allow-other-keys)
"Output and kill message with temporary buffer."
(save-excursion
(with-temp-buffer
(rename-buffer "*DeepL Translate*")
(switch-to-buffer (current-buffer))
(let ((translated-text (cdr (assoc 'text (aref (cdr (assoc 'translations data)) 0)))))
(insert translated-text)
(when (y-or-n-p "Use this translation?")
(kill-new translated-text))))))
(defun my:japanese-character-p (char)
(or (<= #x3041 char #x309f) ; hiragana
(<= #x30a1 char #x30ff) ; katakana
(<= #x4e01 char #x9faf) ; kanji
))
(defun my:deepl-translate (start end)
"Translate region via deepl."
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
;; 3文字以上日本語が含まれている場合は日本語と判断する。
(if (>= (cl-count-if #'my:japanese-character-p region) 3)
(my:deepl-translate-internal region "JA" "EN" #'my:deepl-output-message)
(my:deepl-translate-internal region "EN" "JA" #'my:deepl-output-message))))
(defun my:consult-project-buffer-dwim ()
"Consult a project buffer with DWIM (Do What I Mean) behavior.
The selected buffer should be added to `projectile-known-projects' and
`projectile-project-root-files'.
When no projectile projects are found, fall back to 'consult-buffer'."
(interactive)
(if (and (fboundp 'project-current) (project-current))
(consult-project-buffer)
(consult-buffer)))
主に編集で利用する関数群。s.elの利用などもするが、基本的にはmodal editingで利用するための関数がほとんどである。
(defun my:kill-whole-line-or-region ()
"regionがあればresionを、なければ行全体をkillする"
(interactive)
(if (region-active-p)
(let ((beg (region-beginning))
(end (region-end)))
(kill-region beg end))
(kill-whole-line)))
移動のための関数を定義する。
(defun my:page-up ()
"scroll-up-commandを連続して実行することを可能にするcommand.
Ref: https://github.com/xahlee/xah-fly-keys/blob/master/xah-fly-keys.el
"
(interactive)
(scroll-up-command)
(set-transient-map
(let ((kmap (make-sparse-keymap)))
(keymap-set kmap "<up>" #'my:page-up)
(keymap-set kmap "<down>" #'my:page-down)
kmap)))
(defun my:page-down ()
"scroll-down-commandを連続して実行することを可能にするcommand.
Ref: https://github.com/xahlee/xah-fly-keys/blob/master/xah-fly-keys.el
"
(interactive)
(scroll-down-command)
(set-transient-map
(let ((kmap (make-sparse-keymap)))
(keymap-set kmap "<up>" #'my:page-up)
(keymap-set kmap "<down>" #'my:page-down)
kmap)))
org用にtransientで定義していたときのような動作を簡単に定義した関数。
(defsubst my:org-get-transient-navigation-map ()
"navigation用のtraisient-mapとして利用するkeymapを返す"
(let ((map (make-sparse-keymap)))
(key-layout-mapper-keymap-set map "k" 'my:org-next-visible-heading)
(key-layout-mapper-keymap-set map "j" 'my:org-previous-visible-heading)
(key-layout-mapper-keymap-set map "h" #'my:org-up-heading)
map))
(defun my:org-next-heading ()
"次のheadに移動する。連続して実行できるようになっている。"
(interactive)
(org-next-visible-heading)
(set-transient-map
(my:org-get-transient-navigation-map)))
(defun my:org-previous-heading ()
"前のheadに移動する。連続して実行できるようになっている。"
(interactive)
(org-previous-visible-heading)
(set-transient-map
(my:org-get-transient-navigation-map)))
(defun my:org-up-heading ()
"一つ上の階層に移動する"
(interactive)
(outline-up-heading)
(set-transient-map
(my:org-get-transient-navigation-map)))
(with-eval-after-load 'org
(defun my:tangle-init-org ()
(when (or (string=
(expand-file-name "init.org" user-emacs-directory)
(buffer-file-name))
(string=
(expand-file-name "early-init.org" user-emacs-directory)
(buffer-file-name)))
(when-let* ((fname (file-name-base (buffer-file-name)))
(elc (seq-concatenate 'string fname ".elc"))
(byte-compiled-file (expand-file-name elc user-emacs-directory)))
(when (file-exists-p byte-compiled-file)
(delete-file byte-compiled-file)))
(org-babel-tangle)))
(add-hook 'after-save-hook #'my:tangle-init-org)
)
(eval-when-compile
(elpaca (modus-themes :type git :host github :repo "protesilaos/modus-themes"
:ref "817ff75d11599b65acf583e0f8b5d69163550299")))
(defun my:modus-mode-line-override ()
"mode lineの表示が微妙だったので調整するhook"
(let ((line (face-attribute 'mode-line :underline)))
(set-face-attribute 'mode-line nil :overline line :box nil )
(set-face-attribute 'mode-line-inactive nil :overline line :box nil :underline line)))
(with-eval-after-load 'modus-themes
(setopt modus-themes-slanted-constructs t)
(setopt modus-themes-bold-constructs t)
(setopt modus-themes-mixed-fonts nil)
(setopt modus-themes-variable-pitch-ui nil)
(set-face-attribute 'modus-themes-completion-selected nil :inherit nil) )
(with-low-priority-startup
(load-package modus-themes)
(add-hook 'modus-themes-post-load-hook #'my:modus-mode-line-override)
;; load-themeだとhookが動かない様子なので、一旦これを利用する
(modus-themes-select 'modus-vivendi-tinted))
原則は、1packageにつき1見出しであり、関連するパッケージはleaf側でくくるようにする。
major-modeなどという単位は、org側のoutlineで設定するようにする。
modusの作者が開発している、window/frameの間隔を調整するためのpackage。
https://github.com/protesilaos/spacious-padding?tab=readme-ov-file
(eval-when-compile
(elpaca (spacious-padding :ref "a3151f3c99d6b3b2d4644da88546476b3d31f0fe")))
(with-eval-after-load 'spacious-padding
(setopt spacious-padding-widths '(
:internal-border-width 15
:header-line-width 4
;; 設定しているmode lineとの相性が悪いので、0にしている
:mode-line-width 0
:tab-width 4
:right-divider-width 30
:left-fringe-width 8
:right-fringe-width 8
:scroll-bar-width 8))
)
(with-high-priority-startup
(load-package spacious-padding)
(spacious-padding-mode +1))
(eval-when-compile
(elpaca (dash :ref "1de9dcb83eacfb162b6d9a118a4770b1281bcd84")))
(with-low-priority-startup
(load-package dash)
(require 'dash))
(eval-when-compile
(elpaca (f :ref "1e7020dc0d4c52d3da9bd610d431cab13aa02d8c")))
(with-eval-after-load 'f)
(with-low-priority-startup
(load-package f))
(eval-when-compile
(elpaca (s :ref "dda84d38fffdaf0c9b12837b504b402af910d01d")))
(with-high-priority-startup
(load-package s)
(require 's))
Growlに対応するprotocolを提供するpackage。
(eval-when-compile
(elpaca (gntp :ref "767571135e2c0985944017dc59b0be79af222ef5")))
(with-high-priority-startup
(load-package gntp))
(eval-when-compile
(elpaca (ht :ref "1c49aad1c820c86f7ee35bf9fff8429502f60fef")))
(with-low-priority-startup
(load-package ht))
(eval-when-compile
(elpaca (xterm-color :ref "2ad407c651e90fff2ea85d17bf074cee2c022912")))
(with-low-priority-startup
(load-package xterm-color))
自作したkey layoutを切り替えて同一の場所のキーバインドを設定できるようにするライブラリー。
(eval-when-compile
(elpaca (key-layout-mapper :type git :host github :repo "derui/key-layout-mapper"
:ref "589ca0b56a7885fa8444b077a284e6e47310c8c9")))
(with-eval-after-load 'key-layout-mapper
(key-layout-mapper-set-layout 'sturdy))
(with-low-priority-startup
(load-package key-layout-mapper)
(require 'key-layout-mapper))
magitで使われているUIをlibraryにしたもの。
(eval-when-compile
(elpaca (transient :type git :host github :repo "magit/transient" :branch "main"
:ref "3430943eaa3222cd2a487d4c102ec51e10e7e3c9")))
(with-low-priority-startup
(load-package transient)
(eval-when-compile
(autoload 'transient-define-prefix "transient")))
(with-low-priority-startup
(transient-define-prefix my:org-transient ()
"Prefix for Org-mode related"
[["Navigation"
("J" "Forward heading same level" org-forward-heading-same-level :transient t)
("K" "Backward heading same level" org-backward-heading-same-level :transient t)
("j" "Next heading" org-next-visible-heading :transient t)
("k" "Previous heading" org-previous-visible-heading :transient t)
("u" "Up level" outline-up-heading :transient t)
("l" "Change TODO state" org-cycle :transient t)
("h" "Org heading" consult-org-heading)
]
["Change tree status"
("d" "Done TODO" my:org-done-todo)
("n" "Toggle narrow subtree" org-toggle-narrow-to-subtree)
]
["Clock"
("i" "Clock-in current heading" org-clock-in)
("o" "Clock-out current clock" org-clock-out)]
]
))
markしたりnarrow/widenしたりするcommandをまとめたtransient.
(with-low-priority-startup
(transient-define-prefix my:mark/replace-transient ()
"The prefix for mark/replace related commands"
[
["Narrow/Widen"
("n" "Narrow to region" narrow-to-region)
("w" "Widen" widen)
]
["Replace"
("r" "Replace by visual" anzu-query-replace)
("t" "Replace thing at point by visual" anzu-query-replace-at-cursor-thing)
]
]))
consultなどでの、buffer/fileなどでの移動をまとめるTransient
(with-low-priority-startup
(transient-define-prefix my:navigation-transient ()
"The prefix for navigation via consult and other commands."
[
["Consult"
("b" "Buffer" consult-buffer)
("h" "Recentf" consult-recent-file)
("l" "Line" consult-line)
("o" "Outline" consult-outline)
("s" "Ripgrep" consult-ripgrep)
("F" "Search file by Fd" consult-fd)
("i" "Imenu list" consult-imenu)
]
["File and directory"
("e" "find file" find-file)
("d" "Dired jump" dired-jump)
("f" "Find file for project" project-find-file)
]
["Search by command"
("R" "Find by ripgrep" ripgrep-regexp)
]
])
)
perspective関連のcommandをまとめるTransient.
(with-low-priority-startup
(transient-define-prefix my:persp-transient ()
"The prefix for perspective command."
[
["Buffer navigation"
("b" "Switch buffer" activities-switch-buffer)
]
["Manage perspective"
("o" "Create and open perspective" activities-define)
("k" "Kill perspective" activities-kill)
("R" "Rename perspective" activities-rename)
("r" "Resume perspective" activities-resume)
]
["Move between perspectives"
("s" "Switch between tabs" tab-bar-switch-to-tab)
("h" "Switch previous workspace" tab-bar-switch-to-prev-tab)
("l" "Switch next workspace" tab-bar-switch-to-next-tab)
]
]))
(with-low-priority-startup
(transient-define-prefix my:development-transient ()
"The prefix for project-related command"
[
["Open Project"
("o" "Open project" project-switch-project)
]
["Manage Project"
("D" "Forget project" project-forget-project)
("Z" "Forget zombie projects" project-forget-zombie-projects)
]
["LSP"
("R" "Restart lsp" eglot)
("r" "Rename" eglot-rename)]
["Show Diagnostics"
("a" "Show project-wide diagnostics" flymake-show-project-diagnostics)
("c" "Show buffer-wide diagnostics" flymake-show-buffer-diagnostics)
]])
)
主にace-windowを利用する前提で設定された、Window用のtransient.
(with-low-priority-startup
(defun my:split-window-right-and-switch-buffer ()
"Split the current window rightwards and switch to the new window."
(interactive)
(delete-other-windows)
(split-window-right)
(other-window 1)
(consult-buffer))
(defun my:split-window-below-and-switch-buffer ()
"Split the current window below and switch to the new window."
(interactive)
(delete-other-windows)
(split-window-below)
(other-window 1)
(consult-buffer))
(transient-define-prefix my:window-transient ()
"Transient for window management"
[
["Basic navigations"
("<return>" "Select window by key" ace-window)
("h" "Select left" windmove-left)
("j" "Select down" windmove-down)
("k" "Select up" windmove-up)
("l" "Select right" windmove-right)]
["Split window"
("s" "Split vertically" split-window-vertically)
("v" "Split horizontally" split-window-horizontally)
("S" "Split vertically and switch to other" my:split-window-below-and-switch-buffer)
("V" "Split vertically and switch to other" my:split-window-right-and-switch-buffer)
]
["Manipulate window"
("d" "Delete current window" delete-window)
("D" "Select and delete window" ace-delete-window)
("b" "Balance window" balance-windows)
("o" "Only current window" delete-other-windows)
("O" "Select and only the window" ace-delete-other-windows)]]))
puniが提供するStructuring edit を継続して実行するTransient。
(with-low-priority-startup
(transient-define-prefix my:structuring-transient ()
"The prefix for structuring editing command"
[
["Quit"
("q" "Quit" ignore)
("<escape>" "Quit" ignore)]
["Move with structuring"
("h" "backward char" backward-char :transient t)
("j" "Next sexp" puni-forward-sexp :transient t)
("k" "Previous sexp" puni-backward-sexp :transient t)
("l" "Forward char" forward-char :transient t)
("H" "Beginning of sexp" puni-beginning-of-sexp :transient t)
("L" "End of sexp" puni-end-of-sexp :transient t)
("," "Backward punct" puni-syntactic-backward-punct :transient t)
("." "Forward punct" puni-syntactic-forward-punct :transient t)
]
["Basic editing"
("D" "Kill line balanced" puni-kill-line :transient t)
("x" "Delete character force" (lambda () (interactive) (forward-char) (puni-force-delete)) :transient t)
("d" "Delete backward" puni-backward-delete-char :transient t)
("C-w" "Kill active region" puni-kill-active-region :transient t)
("u" "undo" undo :transient t)
("U" "redo" vundo :transient t)]
["Mark And yank"
("w" "mark and expand thing" my:treesit-expand-region :transient t)
("y" "yank" yank :transient t)]
["Useful editing"
("s" "Sqeeze" puni-squeeze :transient t)
("b" "Barf forward" puni-barf-forward :transient t)
("B" "Barf backward" puni-barf-backward :transient t)
("f" "Slurp forward" puni-slurp-forward :transient t)
("F" "Slurp backward" puni-slurp-backward :transient t)]
["Advanced editing"
("r" "Raise current exp" puni-raise :transient t)
("(" "Wrap with ())" puni-wrap-round :transient t)
("<" "Wrap with <>" puni-wrap-angle :transient t)
("[" "Wrap with []" puni-wrap-square :transient t)
("{" "Wrap with {}" puni-wrap-curly :transient t)
]]))
isearch用のTransient。基本的にisearch の中で使うことを想定している。
(with-low-priority-startup
(transient-define-prefix my:isearch-transient ()
"isearch menu"
[
["Edit isearch string"
("e" "Edit the search string" isearch-edit-string :transient nil)
("w" "Pull next word or character from buffer" isearch-yank-word-or-char :transient nil)
("s" "Pull next symbol or character from buffer" isearch-yank-symbol-or-char :transient nil)
("l" "Pull rest of line from buffer" isearch-yank-line :transient nil)
("y" "Pull string from kill-ring" isearch-yank-from-kill-ring :transient nil)
("t" "Pull thing from buffer" isearch-forward-thing-at-point :transient nil)
]
["Replace"
("r" "Replace by 'query-replace'" anzu-isearch-query-replace)
("x" "Replace by 'query-replace-regexp'" anzu-isearch-query-replace-regexp)
]
["Misc"
("o" "Start occur" isearch-occur)
("v" "Move result with avy" avy-isearch)
]]
[["Toggle"
("X" "Toggle regexp searching" isearch-toggle-regexp)
("S" "Toggle symbol searching" isearch-toggle-symbol)
("W" "Toggle word searching" isearch-toggle-word)
("F" "Toggle case-fold" isearch-toggle-case-fold)
("H" "Toggle isearch highlight" isearch-exit)
]]
))
ellamaを利用して作成する感じ。
(with-low-priority-startup
(transient-define-prefix my:llm-transient ()
"Menus with LLM"
[
["Code related"
("c" "Complete code" ellama-code-complete :transient nil)
("a" "Add code" ellama-code-add :transient nil)
("i" "Improve code" ellama-code-improve :transient nil)
("e" "Edit code" ellama-code-edit :transient nil)
("R" "Review code" ellama-code-review :transient nil)
]
["Generic usage"
("s" "Summary selected content or buffer" ellama-summarize :transient nil)
("t" "Translate selected content" ellama-translate :transient nil)
]
["Grammar"
("W" "Improve wording" ellama-improve-wording :transient nil)
("I" "Improve grammer" ellama-improve-grammar :transient nil)
]]
[["Misc"
("*" "Open chat" ellama-chat :transient t)
]]
))
主にモードラインに対するパッケージをまとめている。
https://github.com/tarsius/moody
magitのメインコミッターが作成しているパッケージ。基本的には見た目を変えるためだけのものであり、それ以外については自前で色々やる必要がある。
(eval-when-compile
(elpaca (moody :type git :host github :repo "tarsius/moody"
:ref "2f249978531ff1ec9f601c1e8f2ce83a1b50520e")))
(with-eval-after-load 'moody
;; 実際にはFont sizeから導出する。
(setopt moody-mode-line-height (let* ((font (face-font 'mode-line)))
(if font
(* 2 (aref (font-info font) 2))
30)))
(setopt x-underline-at-descent-line t)
;; macOSの場合は若干設定が異なる
(darwin!
(setopt moody-slant-function #'moody-slant-apple-rgb))
)
(with-low-priority-startup
(load-package moody))
moodyを前提にしつつ、doom-modelineを利用しないので、自前で色々設定する
(defgroup my:mode-line nil
"Custom mode line."
:group 'my)
(defface my:buffer-position-active-face nil
"Face for active buffer position indicator."
:group 'my:mode-line)
(defface my:mode-line:vc-icon-face nil
"Face for vcs icon"
:group 'my:mode-line)
(defvar-local my:vc-status-text ""
"Variable to store vc status text.")
(defcustom my:mode-line-read-only-icon " "
"variable for read only icon"
:group 'my:mode-line)
(defcustom my:mode-line-writable-icon " "
"variable for writable icon on mode line"
:group 'my:mode-line)
(defcustom my:mode-line-modified-icon " "
"variable for modified icon on mode line"
:group 'my:mode-line)
(defun my:mode-line-status ()
"Return status icon for mode line status.
This function uses nerd-icon package to get status icon."
(let ((read-only (and buffer-file-name buffer-read-only))
(modified (and buffer-file-name (buffer-modified-p))))
(cond
(modified my:mode-line-modified-icon)
(read-only my:mode-line-read-only-icon)
(t my:mode-line-writable-icon))))
(defun my:update-mode-line-vc-text ()
"Update vcs text is used in mode-line"
(setq my:vc-status-text
(cond
((and vc-mode buffer-file-name)
(let* ((backend (vc-backend buffer-file-name))
(branch-name (if vc-display-status
;; 5 is skipped Gitx
(substring vc-mode 5)
" "))
(state (cl-case (vc-state buffer-file-name backend)
(added " ")
(needs-merge " ")
(needs-update " ")
(removed " ")
(t " "))))
(concat (propertize state 'face 'my:mode-line:vc-icon-face) branch-name)))
(t " ")))
)
(defun my:mode-line-buffer-position-percentage ()
"Return current buffer position in percentage."
(let ((pmax (point-max))
(current (point)))
(format "%d%%%%" (/ (* 100 current) pmax))))
(defvar-local my:mode-line-element-region-info ""
"cache variable for region information to show")
(defun my:update-mode-line-active-region-info ()
"Return active region information if exists."
(setq my:mode-line-element-region-info
(if (region-active-p)
(let* ((region (car (region-bounds)))
(lines (count-lines (car region) (cdr region)))
(chars (seq-length (buffer-substring-no-properties (car region) (cdr region)))))
(format " (L%d, C%d) " lines chars)
)
" "))
(force-mode-line-update))
(run-with-idle-timer 0.5 t #'my:update-mode-line-active-region-info)
(defun my:update-mode-line-multistate ()
"Update multistate state"
;; multistate--stateはinternalな状態なのだが、hookだと渡してくれたりしないため、
;; 自分でstoreしている。内部の情報としてはhtableなのだが、ちょっと内部的な情報すぎるので、
;; 一旦pcaseで自分で対処している
(let ((lighter (pcase multistate--state
(`normal "N-")
(`insert "I-")
(`visual "V-")
(`motion-kill "MK")
(`motion-change "MC")
(`motion-yank "MY")
(_ "U"))))
(setq-local my:mode-line-multistate-state lighter)))
(defvar my:mode-line-multistate-state "")
;; definitions of mode-line elements
(defvar my:mode-line-element-buffer-status '(:eval (concat (my:mode-line-status)
)))
(defvar my:mode-line-element-major-mode '(:eval (concat " " (let ((name mode-name))
(cond
((consp name) (car name))
(t name)))
" ")))
(defvar-local my:mode-line-element-vc-mode '(:eval (moody-tab my:vc-status-text)))
(defvar my:mode-line-element-buffer-position '(:eval (moody-ribbon
(propertize
(my:mode-line-buffer-position-percentage)
'face 'my:buffer-position-active-face)
7)))
(defvar my:mode-line-element-pomodoro '(:eval (if (featurep 'simple-pomodoro)
(simple-pomodoro-mode-line-text)
""
)))
(defvar my:mode-line-element-region '(:eval my:mode-line-element-region-info))
(defvar my:mode-line-element-multistate '(:eval (format "[%s]" my:mode-line-multistate-state)))
(defvar-local my:mode-line-buffer-identification
'(:eval (moody-tab (format "%s %s"
(if (featurep 'nerd-icons)
(or (and (buffer-file-name)
(nerd-icons-icon-for-file (buffer-file-name)))
(nerd-icons-icon-for-mode major-mode))
"")
(car (propertized-buffer-identification (buffer-name)))
)
20 'down))
"mode line element with icon if nerd-icons ie available")
(put 'my:mode-line-buffer-identification 'risky-local-variable t)
(put 'my:mode-line-element-buffer-status 'risky-local-variable t)
(put 'my:mode-line-element-major-mode 'risky-local-variable t)
(put 'my:mode-line-element-vc-mode 'risky-local-variable t)
(put 'my:mode-line-element-buffer-position 'risky-local-variable t)
(put 'my:mode-line-element-pomodoro 'risky-local-variable t)
(put 'my:mode-line-element-region 'risky-local-variable t)
(put 'my:mode-line-element-multistate 'risky-local-variable t)
;; define default mode line format
(defun my:init-mode-line ()
"Initialize mode line"
(set-face-attribute 'my:buffer-position-active-face
nil
:inherit 'mode-line
:foreground (modus-themes-get-color-value 'red-warmer))
(set-face-attribute 'my:mode-line:vc-icon-face
nil
:inherit 'mode-line
:foreground (modus-themes-get-color-value 'fg-alt))
;; replace mode line elements via moody
(moody-replace-mode-line-front-space)
(moody-replace-mode-line-buffer-identification)
(setq-default mode-line-format
'("%e"
moody-mode-line-front-space
my:mode-line-element-multistate
my:mode-line-element-buffer-status
my:mode-line-buffer-identification
my:mode-line-element-region
mode-line-format-right-align
my:mode-line-element-pomodoro
my:mode-line-element-buffer-position
my:mode-line-element-vc-mode
my:mode-line-element-major-mode)))
(with-low-priority-startup
(add-hook 'find-file-hook #'my:update-mode-line-vc-text)
(add-hook 'after-save-hook #'my:update-mode-line-vc-text)
(add-hook 'multistate-change-state-hook #'my:update-mode-line-multistate)
;; should update status text after refresh state
(advice-add #'vc-refresh-state :after #'my:update-mode-line-vc-text)
(my:init-mode-line))
evilからvimのkeybindを除いたようなpackage。純粋にstateとmodalの管理しかしない。
ここでのmultistateの定義としては、以下のStateのみを定義する。
- Emacs
- Normal
- Visual
- Motion
Motionについては、visualを前提とした状態でもある。motionの処理そのものは、stateとして管理はするものの、which-keyを主に利用する形となる。
(eval-when-compile
(elpaca (multistate :type git :host github :repo "emacsmirror/multistate"
:ref "a7ab9dc7aac0b6d6d2f872de4e0d1b8550834a9b")))
(with-eval-after-load 'multistate
(defun my:multistate-disable ()
"multistateを強制的に無効化する"
(multistate-mode -1)))
(with-low-priority-startup
(load-package multistate)
;; global-modeだと特殊なmodeを全部列挙しないといけないので、prog/text/confだけに一回絞っておく
(add-hook 'prog-mode-hook #'multistate-mode)
(add-hook 'text-mode-hook #'multistate-mode)
(add-hook 'conf-mode-hook #'multistate-mode))
(eval-when-compile
(elpaca (motion :type git :host github :repo "derui/motion"
:ref "b67044122700eb02cf18223e5df7c8f8c3541131")))
(with-low-priority-startup
(load-package motion)
(motion-define my:motion-buffer
"Motion for buffer"
:forward
(let ((current (point)))
(cons current (point-max)))
:backward
(let ((current (point)))
(cons (point-min) current)))
(motion-define my:motion-char
"Motion for character"
:forward
(let ((current (point)))
(forward-char arg)
(cons current (point)))
:backward
(let ((current (point)))
(backward-char arg)
(cons (point) current)))
(motion-define-thing my:motion-word 'word)
(motion-define-thing my:motion-symbol 'symbol)
(motion-define-thing my:motion-line 'line)
(motion-define-pair my:motion-single-quote '(?' . ?'))
(motion-define-pair my:motion-double-quote '(?\" . ?\"))
(motion-define-pair my:motion-paren '(?\( . ?\)) t)
(motion-define-pair my:motion-square '(?\[ . ?\]) t)
(motion-define-pair my:motion-curly '(?{ . ?}) t)
(motion-define-pair my:motion-angle '(?< . ?>) t))
multistateで利用するためのmacroを定義する。
(eval-when-compile
(defmacro interactive! (&rest body)
"interactiveなlambdaを定義するためのmacro.
prefixの引数として `it' を受け取ることができる"
`(lambda (&optional it) (interactive "p")
,@body)
)
(defmacro insert-after! (&rest body)
"`body' を実行したあとに、insert stateに入るcommandを定義する"
`(lambda ()
(interactive)
,@body
(multistate-insert-state)))
(defmacro normal-after! (&rest body)
"`body' を実行したあとに、normal stateに入るcommandを定義する"
`(lambda ()
(interactive)
,@body
(multistate-normal-state)))
(defmacro set-key! (keymap key fn)
"key-layout-mapperを前提としたkey bindingを設定するためのmacro"
`(key-layout-mapper-keymap-set ,keymap ,key ,fn)))
navigationなどを行うための基本的なStateである。非常によく利用するcommand以外などではtransientで定義したPrefixなどを適宜利用していく。
(with-eval-after-load 'multistate
(unless (fboundp 'multistate-normal-state)
(multistate-define-state 'normal
:default t
:lighter "N"
:cursor 'box
:parent 'multistate-suppress-map))
;; hook
(declare-function corfu-quit 'corfu)
;; normal stateに戻って来たら補完は消す
(add-hook 'multistate-normal-state-enter-hook #'corfu-quit)
(set-key! multistate-normal-state-map "q" (interactive!
(if (> (seq-length (window-list)) 1)
(quit-window)
(previous-buffer))))
;; right hand definition
;; 右手はnavigation/selectionを前提にする
(set-key! multistate-normal-state-map "j" #'backward-char)
(set-key! multistate-normal-state-map "k" #'next-line)
(set-key! multistate-normal-state-map "i" #'previous-line)
(set-key! multistate-normal-state-map "l" #'forward-char)
(set-key! multistate-normal-state-map "o" #'forward-word)
(set-key! multistate-normal-state-map "u" #'backward-word)
(set-key! multistate-normal-state-map ";" #'end-of-line)
(set-key! multistate-normal-state-map "h" #'back-to-indentation)
(set-key! multistate-normal-state-map "," #'puni-backward-sexp)
(set-key! multistate-normal-state-map "." #'puni-forward-sexp)
(set-key! multistate-normal-state-map "m" #'my:treesit-expand-region)
(set-key! multistate-normal-state-map "/" #'consult-line)
(set-key! multistate-normal-state-map "<" #'mc/mark-previous-like-this)
(set-key! multistate-normal-state-map ">" #'mc/mark-next-like-this)
;; undo/redo
(set-key! multistate-normal-state-map "z z" #'undo)
(set-key! multistate-normal-state-map "z v" #'vundo)
;; advanced move
(set-key! multistate-normal-state-map "f" #'avy-goto-char-timer)
(set-key! multistate-normal-state-map "X" #'goto-line)
(set-key! multistate-normal-state-map "g" #'beginning-of-buffer)
(set-key! multistate-normal-state-map "G" #'end-of-buffer)
(set-key! multistate-normal-state-map "H" #'scroll-down-command)
(set-key! multistate-normal-state-map "M-H" #'scroll-other-window-down)
(set-key! multistate-normal-state-map "L" #'scroll-up-command)
(set-key! multistate-normal-state-map "M-L" #'scroll-other-window)
;; left hand definition
;; 左手はedit/modificationを前提にする
(set-key! multistate-normal-state-map "x" #'kill-region)
(set-key! multistate-normal-state-map "c" #'kill-ring-save)
(set-key! multistate-normal-state-map "v" #'yank)
(set-key! multistate-normal-state-map "a" #'execute-extended-command)
(set-key! multistate-normal-state-map "f" #'multistate-insert-state)
(set-key! multistate-normal-state-map "e" #'delete-char)
(set-key! multistate-normal-state-map "b" #'comment-dwim)
(set-key! multistate-normal-state-map "t" #'my:kill-whole-line-or-region)
(set-key! multistate-normal-state-map "3" #'split-root-window-right)
(set-key! multistate-normal-state-map "4" #'split-root-window-below)
(set-key! multistate-normal-state-map "2" #'ace-window)
(set-key! multistate-normal-state-map "1" #'delete-other-windows)
;; global leader key
(set-key! multistate-normal-state-map "SPC"
(let ((keymap (make-sparse-keymap)))
(set-key! keymap "k" #'kill-current-buffer)
(set-key! keymap "s" #'save-buffer)
(set-key! keymap "o" #'find-file)
(set-key! keymap "d" #'dired-jump)
(set-key! keymap "m" #'magit-status)
(set-key! keymap "i" #'ibuffer)
(set-key! keymap "g" #'consult-ripgrep)
(set-key! keymap "h o" #'beginning-of-buffer)
(set-key! keymap "h l" #'end-of-buffer)
(set-key! keymap "h <up>" #'my:page-up)
(set-key! keymap "h <down>" #'my:page-down)
;; (set-key! keymap "" #'ripgrep-regexp)
(set-key! keymap "f" #'consult-fd)
(set-key! keymap "#" #'server-edit)
(set-key! keymap "v" #'vterm)
(set-key! keymap "r" #'my:mark/replace-transient)
(set-key! keymap "/" #'my:navigation-transient)
(set-key! keymap "." #'my:persp-transient)
(set-key! keymap "'" #'window-toggle-side-windows)
(set-key! keymap "b c" #'org-capture)
(set-key! keymap "b r" #'org-roam-capture)
(set-key! keymap "," #'my:development-transient)
(set-key! keymap ";" #'my:consult-project-buffer-dwim)
(set-key! keymap "t t" #'my:deepl-translate)
(set-key! keymap "t e" #'eval-expression)
(set-key! keymap "t l" #'my:llm-transient)
;; flymake integration
(declare-function flymake-goto-next-error 'flymake)
(declare-function flymake-goto-prev-error 'flymake)
(set-key! keymap "n n" #'flymake-goto-next-error)
(set-key! keymap "n h" #'flymake-goto-prev-error)
;; org-mode
(set-key! keymap "n o k" #'my:org-next-heading)
(set-key! keymap "n o i" #'my:org-previous-heading)
(set-key! keymap "n o d" #'my:org-done-todo)
(set-key! keymap "n o h" #'consult-org-heading)
(set-key! keymap "n o c" #'org-clock-in)
(set-key! keymap "n o t" #'org-clock-out)
keymap
)
)
)
nvim/vimのinsert modeに相当する。ほぼEmacs stateと変らないが、keymap-global-setを利用したくないが、一定広く使いたいkeyを定義するのを想定。
(with-eval-after-load 'multistate
(multistate-define-state 'insert
:lighter "I"
:cursor 'bar
:parent 'multistate-emacs-state-map)
;; ESCでnormal stateに戻る
(keymap-set multistate-insert-state-map "<escape>" #'multistate-normal-state))
Gitのinterface。
https://github.com/magit/with-editor https://github.com/magit/magit
(eval-when-compile
(elpaca (with-editor :type git :host github :repo "magit/with-editor"
:ref "77cb2403158cfea9d8bfb8adad81b84d1d6d7c6a"))
(elpaca (magit :type git :host github :repo "magit/magit"
:ref "93e86ceca7bf5b0ff9023d7f138e5f06990fc276"))
(elpaca (magit-section :type git :host github :repo "magit/magit"
:ref "93e86ceca7bf5b0ff9023d7f138e5f06990fc276"))
)
(with-eval-after-load 'magit
;; magitのbuffer切り替えを変える
(setopt magit-display-buffer-function #'display-buffer)
;; diff hunksをすべて表示するようにする
(setq-default magit-diff-refine-hunk 'all)
(defun my:insert-commit-template-on-magit ()
"Insert commit comment template after opened commit buffer on magit."
(tempel-insert 'cc))
(defun my:git-post-commit--delete-EDITMSG ()
"EDITMSGを削除する"
(when-let* ((target-name "COMMIT_EDITMSG")
(buffer (seq-find (lambda (buf)
(let ((name (buffer-name buf)))
(string-match-p name target-name)))
(buffer-list))))
(condition-case nil
(kill-buffer buffer)
((debug error) nil)
)))
(defun my:disable-multistate-on-commit ()
"commitではmodal editingをinsert stateにする"
(when (and (featurep 'multistate)
(fboundp 'multistate-insert-state))
(multistate-insert-state)))
(add-hook 'git-commit-post-finish-hook #'my:git-post-commit--delete-EDITMSG)
(add-hook 'git-commit-mode-hook #'my:insert-commit-template-on-magit)
(add-hook 'git-commit-mode-hook #'my:disable-multistate-on-commit)
(add-hook 'git-commit-mode-hook #'my:hide-mode-line)
(add-hook 'magit-status-mode-hook #'my:hide-mode-line)
(add-hook 'magit-revision-mode-hook #'my:hide-mode-line)
(add-hook 'magit-log-mode-hook #'my:hide-mode-line)
)
(with-low-priority-startup
(load-package with-editor)
(load-package magit)
(load-package magit-section))
https://github.com/dandavison/magit-delta/tree/master deltaをmagitのdiffとしてつかえるようにする。
(eval-when-compile
(elpaca (magit-delta :ref "5fc7dbddcfacfe46d3fd876172ad02a9ab6ac616")))
(with-eval-after-load 'magit
(add-hook 'magit-mode-hook #'magit-delta-mode))
(with-low-priority-startup
(load-package magit-delta))
https://github.com/minad/consult swiper/counselの置き換え。
(eval-when-compile
(elpaca (consult :ref "4889458dccf842ab6223099f8a73ff8b147e9459")))
(defun my:consult-search-dwim (&optional prefix)
"Merge version to search document via grep/rg.
Use fast alternative if it exists, fallback grep if no alternatives in system.
"
(interactive "P")
(cond
((executable-find "rg") (consult-ripgrep prefix))
(t (consult-grep prefix))))
;; hotfuzz-moduleが有効な場合は、この設定がないとconsultでの検索がerrorになる場合がある
(setq consult--tofu-char #x100000)
(setq consult--tofu-range #x00fffe)
;; recent fileでpreviewする場合は明示的に実行する
(with-eval-after-load 'consult
(setopt consult-fd-args '((if (executable-find "fdfind" 'remote) "fdfind" "fd") "--full-path --color=never -H"))
(setopt consult-ripgrep-args
"rg --null --line-buffered --color=never --max-columns=1000 --path-separator / --smart-case --no-heading --with-filename --line-number --hidden")
;; previewはC-.を押したときだけ
(setopt consult-preview-key "C-."))
(with-low-priority-startup
(load-package consult))
https://github.com/oantolin/embark Contextに応じたアクションを実行できる、というようなもの。embark-actを実行して、そこに対して特定のキーにバインドされているアクションを実行する形。大体はembark-exportでやればよい。
- B
embark-become
- S
embark-collect-snapshot
- L
embark-collect-live
- E
embark-export
というのがデフォルトのバインディングになっている。
(eval-when-compile
(elpaca (embark :ref "19a13e344e04bbf861eaa74491b23da52b398672"))
(elpaca (embark-consult :ref "19a13e344e04bbf861eaa74491b23da52b398672")))
(with-low-priority-startup
(load-package embark)
(load-package embark-consult)
(keymap-global-set "M-a" #'embark-act)
(keymap-global-set "<f1> B" #'embark-bindings)
(add-hook 'embark-collect-mode-hook #'consult-preview-at-point-mode))
やりたいことベースでメモる。
- consultで検索した結果をoccurして一括編集
consult-line
(C-s) →embark-export
- C-S-aしてからすぐ
E
- C-S-aしてからすぐ
- consultでファイルから検索した結果を一括編集
consult-ripgrep
(, s) →embark-export
- C-S-aしてからすぐ
E
- C-S-aしてからすぐ
大体はexportするとwgrep/occur-editとかができるようになる、と覚えればよし。
https://github.com/minad/marginalia minibufferの表示に対して注釈?を追加できるパッケージ。consult/embarkそれぞれのパッケージで利用が強く推奨されているので。
注釈というか、metaという情報らしい。
(eval-when-compile
(elpaca (marginalia :ref "50a51c69f006ec8b3ba1c570555d279d4cff6d99")))
(with-eval-after-load 'marginalia
(add-to-list 'marginalia-prompt-categories
'("\\<File\\>" . file)))
(with-low-priority-startup
(load-package marginalia)
(marginalia-mode +1)
;; Either bind `marginalia-cycle` globally or only in the minibuffer
(keymap-set minibuffer-local-map "M-A" #'marginalia-cycle))
https://github.com/minad/vertico
垂直補完UIを提供することのみを目的としたUIライブラリ。
(eval-when-compile
(elpaca (vertico :type git :host github :repo "minad/vertico"
:ref "c682ef50e62237435e9fc287927ce4181b49be90")))
(with-eval-after-load 'vertico
;; 選択時にprefix iconを表示する
;; https://github.com/minad/vertico/wiki#prefix-current-candidate-with-arrow
(defvar +vertico-current-arrow t)
(with-eval-after-load 'nerd-icons
(cl-defmethod vertico--format-candidate :around
(cand prefix suffix index start &context ((and +vertico-current-arrow
(not (bound-and-true-p vertico-flat-mode)))
(eql t)))
(setq cand (cl-call-next-method cand prefix suffix index start))
(let ((arrow (nerd-icons-faicon "nf-fa-hand_o_right")))
(if (bound-and-true-p vertico-grid-mode)
(if (= vertico--index index)
(concat " " arrow " " cand)
(concat #("_" 0 1 (display " ")) cand))
(if (= vertico--index index)
(concat " " arrow " " cand)
(concat " " cand))))))
;; 最大20件まで表示するように
(setopt vertico-count 20)
;; vertico内でdirectory 移動を効率的に行うことができるようにする
(keymap-set vertico-map "RET" #'vertico-directory-enter)
(keymap-set vertico-map "<backspace>" #'vertico-directory-delete-char)
(keymap-set vertico-map "M-DEL" #'vertico-directory-delete-char)
;; minibufではなく標準のバッファで表示する
(vertico-buffer-mode +1)
;; bufferは分割の方向が混乱してしまうときが結構あるので、bottom固定とする
;; side window設定もできるのだが、そうしてしまうと、window の中身がずれてしまってかなりストレスだったので、
;; 通常のwindow にしている
(setopt vertico-buffer-display-action `(display-buffer-at-bottom
(window-height . ,(+ 3 vertico-count))))
(vertico-multiform-mode +1)
(add-to-list 'vertico-multiform-categories '(jinx grid (vertico-grid-annotate . 20)))
)
(with-low-priority-startup
(load-package vertico)
(vertico-mode +1))
https://github.com/oantolin/orderless completionのstyleを変更するパッケージ。基本的には空白区切りでのfilteringを提供する。
(eval-when-compile
(elpaca (orderless :ref "416c62a4a8e7199567a5df63d03cf320dc4d6ab0")))
(defun my:orderless-migemo (component)
(if (featurep 'migemo)
(condition-case nil
(let ((pattern (migemo-get-pattern component)))
(progn (string-match-p pattern "") pattern))
(nil nil))
nil))
(with-eval-after-load 'orderless
(setq completion-category-overrides
'((command (styles orderless-default-style))
;; ファイルの場合には、pathの部分matchをするように
(file (styles orderless-migemo-style))
(org-roam-node (styles . (partial-completion orderless-migemo-style)))
(buffer (styles orderless-migemo-style))
(symbol (styles orderless-default-style))
(consult-location (styles orderless-migemo-style)) ; category `consult-location' は `consult-line' などに使われる
(consult-multi (styles orderless-migemo-style)) ; category `consult-multi' は `consult-buffer' などに使われる
(unicode-name (styles orderless-migemo-style))
(variable (styles orderless-default-style)))))
(with-low-priority-startup
(load-package orderless)
(require 'orderless)
(orderless-define-completion-style orderless-default-style
(orderless-matching-styles '(orderless-literal
orderless-regexp)))
(orderless-define-completion-style orderless-migemo-style
(orderless-matching-styles '(orderless-literal
orderless-regexp
my:orderless-migemo))))
https://github.com/axelf4/hotfuzz built-inのflexに似た結果を生成するが、より高速かつ、単語間の切れめなどがよりわかりやすいようになっている。
(eval-when-compile
(elpaca (hotfuzz :type git :host github :repo "axelf4/hotfuzz" :branch "master"
:files ("hotfuzz.el" "hotfuzz-module.c" "CMakeLists.txt")
:ref "622329477d893a9fc2528a75935cfe1f8614f4bc")))
(with-low-priority-startup
(load-package hotfuzz)
(add-to-list 'completion-styles 'hotfuzz)
)
https://github.com/minad/corfu minimalなregion completion。child frameを利用しているのと、あくまでシンプルなUIのみを提供しているため、軽量かつ高速。
(eval-when-compile
(elpaca (corfu :type git :host github :repo "minad/corfu" :branch "main"
:ref "fa2be6c66ff2eb10b4b609832f25df49e90381af")))
(with-eval-after-load 'corfu
(setopt corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
(setopt corfu-auto t) ;; Enable auto completion
(setopt corfu-auto-delay 0.1) ;; 即時表示を試してみる
(setopt corfu-count 15) ;; show more candidates
;; 2文字の入力でcorfuを表示する
(setopt corfu-auto-prefix 2)
(setopt corfu-max-width 300) ;; max width of corfu completion UI
;; 単独で厳密マッチしたものがあった場合でも明示的な補完アクションを要求する
(setopt corfu-on-exact-match nil)
;; 最初の候補を選択しない
(setopt corfu-preselect 'directory)
(defvar corfu--index)
(defvar corfu-magic-insert-or-next-line
`(menu-item "" nil :filter ,(lambda (&optional _)
(when (>= corfu--index 0)
'corfu-insert)))
"If we made a selection during `corfu' completion, select it.")
(keymap-set corfu-map "RET" corfu-magic-insert-or-next-line)
(defvar corfu-magic-cancel-or-backspace
`(menu-item "" nil :filter ,(lambda (&optional _)
(when (>= corfu--index 0)
'corfu-reset)))
"If we made a selection during `corfu' completion, cancel it.")
(keymap-set corfu-map "DEL" corfu-magic-cancel-or-backspace)
(keymap-set corfu-map "<backspace>" corfu-magic-cancel-or-backspace))
(with-low-priority-startup
(load-package corfu)
(add-hook 'corfu-mode-hook #'corfu-popupinfo-mode)
(global-corfu-mode +1))
https://github.com/minad/cape capf = completion-at-point-functionを極限までシンプルに拡張するための処理。corfuなどとは独立していて、あくまでcapfを拡張するだけに留まっている。
(eval-when-compile
(elpaca (cape :ref "9110956a5155d5e3c460160fa1b4dac59322c229")))
(declare-function eglot-completion-at-point 'eglot)
(declare-function tempel-complete 'tempel)
(with-eval-after-load 'eglot
(defun my:eglot-capf ()
"set capf for eglot"
(setq-local completion-at-point-functions
(list (cape-capf-case-fold
(cape-capf-super
#'eglot-completion-at-point
#'tempel-complete
#'cape-file)))))
(add-hook 'eglot-managed-mode-hook #'my:eglot-capf))
(with-low-priority-startup
(load-package cape)
;; Add `completion-at-point-functions', used by `completion-at-point'.
(add-to-list 'completion-at-point-functions #'tempel-complete)
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-keyword))
org-mode本体の設定をおこなう。
(eval-when-compile
(elpaca (org :ref "233a0ced97366090c31ef94562879bb2f729b120")))
(with-eval-after-load 'org
;; org-mode内部のソースを色付けする
(setopt org-src-fontify-natively t)
;; org-modeの開始時に、行の折り返しを無効にする。
(setopt org-startup-truncated t)
;; follow-linkから戻ることを可能とする。
(setopt org-return-follows-link t)
;; 自動的にタグをalignしない
(setopt org-auto-align-tags nil)
;; tagをalign するカラム
(setopt org-tags-column 0)
(setopt org-catch-invisible-edits 'show-and-error)
;; 先頭にあるstarを隠す
(setopt org-hide-leading-stars t)
;; org特有のCtrl-a/eの挙動を使う
(setopt org-special-ctrl-a/e t)
;; 現在のsubtreeの後にheadingを追加するようにする
(setopt org-insert-heading-respect-content t)
;; UTF8にあるentitiesを利用するようにする
(setopt org-pretty-entities t)
;; outlineのellipsisで使う文字を指定する
(setopt org-ellipsis "…")
;; refileするときにfileを使う
(setopt org-refile-use-outline-path 'file)
(setopt org-outline-path-complete-in-steps nil)
;; doneしたら時刻を記録する
(setopt org-log-done 'time)
;; TODOにおける区別
(setopt org-todo-keywords '((sequence "TODO(t)" "WAITING(w)" "|" "DONE(d)" "CANCELED(c)")))
;; nodeのLevel に応じたインデントは行わない
(setopt org-adapt-indentation nil)
;; <の後で使えるテンプレート
(setopt org-structure-template-alist '(("s" . "src")
("e" . "example")
("c" . "center")
("q" . "quote")
("v" . "verse")
("C" . "comment")
("E" . "export")
("l" . "src emacs-lisp")
("h" . "export html")
("a" . "export ascii")))
;; 余計なmodule を初期から読み込まないために空にしている
(setopt org-modules '())
;; electric-pair-modeで各Syntaxをwrapできるようにする
(modify-syntax-entry ?/ "\"" org-mode-syntax-table)
(modify-syntax-entry ?* "\"" org-mode-syntax-table)
(modify-syntax-entry ?= "\"" org-mode-syntax-table)
(modify-syntax-entry ?+ "\"" org-mode-syntax-table)
(modify-syntax-entry ?_ "\"" org-mode-syntax-table)
(modify-syntax-entry ?~ "\"" org-mode-syntax-table)
)
(with-low-priority-startup
(load-package org)
(add-hook 'org-mode-hook #'electric-pair-local-mode))
(with-eval-after-load 'org
(org-babel-do-load-languages 'org-babel-load-languages '((plantuml . t)))
(setq org-plantuml-jar-path (expand-file-name (locate-user-emacs-file "plantuml.jar")))
)
(with-low-priority-startup
(defun my:org-capture ()
"do capture fastest"
(interactive)
(org-capture nil "t"))
(defun my:org-done-todo ()
(interactive)
(org-todo "DONE"))
(defun my:org-current-is-todo ()
(string= "TODO" (org-get-todo-state)))
(defun my:org-roam-buffer-p (&optional buffer)
"Return boolean that current buffer is roam buffer or not"
(with-current-buffer (or buffer (current-buffer))
(and buffer-file-name
(string= (expand-file-name (file-name-as-directory my:org-roam-directory))
(expand-file-name (file-name-directory buffer-file-name))))))
(defun my:org-roam-project-file-p (&optional buffer)
"Return non-nil if current buffer has any todo entry"
(org-element-map
(org-element-parse-buffer 'headline)
'headline
(lambda (e) (eq (org-element-property :todo-type e) 'todo))
nil 'first-match))
(defun my:org-roam-update-roam-tags (&rest tags)
"Update filetags with TAGS list"
(let* ((tags (combine-and-quote-strings tags " ")))
(my:org-set-keyword "filetags" tags)))
(defun my:org-roam-project-update-tag ()
"Update PROJECT tag in the current buffer."
(when (and (not (active-minibuffer-window))
(my:org-roam-buffer-p))
(save-excursion
(goto-char (point-min))
(let* ((tags (or (my:org-get-keyword "filetags") ""))
(tags (--map (s-replace-all '(("\"" . "")) it) (s-split " " tags)))
(original-tags tags))
(if (my:org-roam-project-file-p)
(setq tags (seq-uniq (cons "project" tags)))
(setq tags (remove "project" tags)))
(unless (equal original-tags tags)
(apply #'my:org-roam-update-roam-tags tags))))))
(defun my:org-roam-project-files ()
"Return a list of note files containing 'project' tag." ;
(seq-uniq
(seq-map
#'car
(org-roam-db-query
[:select [nodes:file]
:from tags
:left-join nodes
:on (= tags:node-id nodes:id)
:where (like tag (quote "%\"project\"%"))]))))
(defun my:org-set-keyword (keyword value &optional buffer)
"Add or replace VALUE of KEYWORD of org-mode to current buffer. "
(save-excursion
(with-current-buffer (or buffer (current-buffer))
(let* ((org-tree (org-element-parse-buffer))
(el (org-element-map
org-tree
'keyword
(lambda (el) (let ((keyword-in-el (org-element-property :key el)))
(and (string-match-p keyword keyword-in-el)
el)))
nil 'first-match)))
(when el
(delete-region (org-element-property :begin el) (org-element-property :end el))
(setq org-tree (org-element-parse-buffer)))
(let* ((first-keyword (org-element-map org-tree 'keyword #'identity nil t))
(el (if (not el)
(let* ((el (org-element-create 'keyword))
(el (org-element-put-property el :key keyword))
(el (org-element-put-property el :value value)))
(goto-char (1+ (org-element-property :end first-keyword)))
(newline)
(insert (org-element-interpret-data el)))
(org-element-put-property el :value value))))
(goto-char (org-element-property :end first-keyword))
(insert (org-element-interpret-data el))
(save-buffer))))))
(defun my:org-get-keyword (keyword &optional buffer)
"Get KEYWORD from BUFFER or current buffer. You can use regexp or raw string for KEYWORD."
(with-current-buffer (or buffer (current-buffer))
(let ((el (org-element-map
(org-element-parse-buffer)
'keyword
(lambda (el)
(when (string-match-p (s-upcase keyword) (org-element-property :key el)) el)) nil 'first-match)))
(when el
(org-element-property :value el)))))
(defun my:org-global-props (&optional property buffer)
"Get the plists of global org properties of current buffer."
(unless property (setq property "PROPERTY"))
(with-current-buffer (or buffer (current-buffer))
(org-element-map
(org-element-parse-buffer)
'keyword
(lambda (el) (when (string-match property (org-element-property :key el)) el)))))
(defun my:org-add-ymd-to-archive (name)
"replace anchor to YYYY-MM string"
(let* ((ymd (format-time-string "%Y-%m")))
(replace-regexp-in-string "#YM" ymd name)))
(with-eval-after-load 'org
(advice-add 'org-extract-archive-file :filter-return #'my:org-add-ymd-to-archive))
(add-hook 'after-save-hook #'my:org-roam-project-update-tag)
)
;; refileする対象を指定する
(with-eval-after-load 'org
(let ((project (expand-file-name "project.org" my:org-roam-directory)))
(setq org-refile-targets
`((,project :maxlevel . 1))))
)
;; archiveするときの設定
(with-eval-after-load 'org
(when my:org-roam-directory
(progn
(let ((inbox (expand-file-name "inbox.org" my:org-roam-directory)))
(setq org-capture-templates
`(("t" "todo" plain (file ,inbox)
"* TODO %?\n%U\n" :clock-resume t))))
(defun my:org-set-archive-name-for-month (&rest args)
(setq-local org-archive-location (concat "./archives/"
(format-time-string "%Y%m" (current-time))
"-%s_archive::datetree/* Finished Tasks")))
(advice-add 'org-archive-subtree :before #'my:org-set-archive-name-for-month)))
)
(defvar my:org-clocked-time-mode-line ""
"org-clock-inしているときのmode line")
(defun my:org-clock-out-and-save-when-exit ()
"Save buffers and stop clocking when kill emacs."
(when (and (fboundp 'org-clocking-p)
(org-clocking-p))
(org-clock-out)
(save-some-buffers t)))
(declare-function org-clock-get-clocked-time 'org-clock)
(defvar org-clock-effort)
(defun my:task-clocked-time ()
"format of clocked time"
(interactive)
(let* ((clocked-time (org-clock-get-clocked-time))
(h (truncate clocked-time 60))
(m (mod clocked-time 60))
(work-done-str (format "%d:%02d" h m)))
(if org-clock-effort
(let* ((effort-in-minutes
(org-duration-to-minutes org-clock-effort))
(effort-h (truncate effort-in-minutes 60))
(effort-m (truncate (mod effort-in-minutes 60)))
(effort-str (format "%d:%02d" effort-h effort-m)))
(format "%s/%s" work-done-str effort-str))
(format "%s" work-done-str))))
(defun my:update-task-clocked-time ()
(setq my:org-clocked-time-mode-line (my:task-clocked-time)))
(with-eval-after-load 'org-clock
;; 0秒は記録しない
(setopt org-clock-out-remove-zero-time-clocks t)
;; frameのtitleに経過時間を表示する
(setopt org-clock-clocked-in-display 'frame-title)
(setopt org-clock-frame-title-format '((:eval (format "%s %s"
(if (require 'org-clock-today nil t)
(if org-clock-today-count-subtree
(format "%s / %s"
org-clock-today-subtree-time
org-clock-today-buffer-time)
(format "%s" org-clock-today-buffer-time))
"")
org-mode-line-string)))))
(with-low-priority-startup
(add-hook 'org-clock-out-hook #'org-update-all-dblocks)
(add-hook 'kill-emacs-hook #'my:org-clock-out-and-save-when-exit))
(with-low-priority-startup
(with-eval-after-load 'org
(require 'org-tempo)))
(eval-when-compile
(elpaca (org-onit :type git :host github :repo "takaxp/org-onit"
:ref "932ed472e46c277daf1edf0efb71fbac5ff45346")))
(with-eval-after-load 'org
(declare-function org-onit-goto-anchor 'org-onit)
(keymap-set org-mode-map "<f11>" #'org-onit-toggle-doing)
(keymap-set org-mode-map "S-<f11>" #'org-onit-goto-anchor)
)
(with-low-priority-startup
(load-package org-onit))
(eval-when-compile
(elpaca (ox-hugo :ref "c4156d9d383bf97853ba9e16271b7c4d5e697f49"))
(elpaca (tomelr :ref "670e0a08f625175fd80137cf69e799619bf8a381")))
(defun my:org-hugo-enable-if-hugo-buffer ()
(let ((prop (my:org-global-props "HUGO_.\+" (current-buffer))))
(when prop
(org-hugo-auto-export-mode +1))))
(with-eval-after-load 'ox-hugo)
(with-low-priority-startup
(load-package tomelr)
(load-package ox-hugo)
(add-hook 'org-mode-hook #'my:org-hugo-enable-if-hugo-buffer))
emacsqlのバックエンドとしてsqliteを使うが、使うsqliteとしてemacsにbuiltinされているものを使う、というやつ。
(eval-when-compile
(elpaca (emacsql :ref "5108c16c5e1d5bfdd41fcc0807241e28886ab763")))
(with-eval-after-load 'emacsql-sqlite-builtin)
(with-low-priority-startup
(load-package emacsql))
https://github.com/org-roam/
Roamというtoolをorg-mode上で再現しようとしているもの。
(eval-when-compile
(elpaca (org-roam :type git :host github :repo "org-roam/org-roam"
:ref "0b9fcbc97b65b349826e63bad89ca121a08fd2be")))
(with-eval-after-load 'org-roam
(setopt org-roam-directory my:org-roam-directory)
(setopt org-roam-db-update-on-save t)
(setopt org-roam-db-location my:org-roam-db-location)
(setopt org-roam-database-connector 'sqlite-builtin)
(setopt org-roam-capture-ref-templates '(("r" "ref" plain "%?"
:if-new (file+head "%<%Y-%m-%d--%H-%M-%SZ>--${slug}.org" "#+title: ${title}\n#+filetags: \n#+roam_key: ${ref}")
:unnarrowed t)))
(setopt org-roam-capture-templates '(("d" "default" plain
"%?"
:if-new (file+head "%<%Y-%m-%d--%H-%M-%SZ>--${slug}.org" "#+title: ${title}\n#+filetags: \n")
:unnarrowed t)))
)
(with-eval-after-load 'org
(keymap-set org-mode-map "C-c r" #'org-roam-node-insert)
(keymap-set org-mode-map "C-c t" #'org-roam-tag-add)
)
(with-low-priority-startup
(load-package org-roam)
(org-roam-db-autosync-mode +1))
https://github.com/minad/org-modern
org-modeの表現をモダンなものにしてくれるパッケージ。variable pitch的な挙動になるので、結構気をつける必要がある。
(eval-when-compile
(elpaca (org-modern :ref "e306c7df4985f77e5c4e2146900259a23a76c974")))
(with-eval-after-load 'org-modern
(setopt org-modern-block-fringe t)
;; UDEV Gothicだとガタつくので、ガタつかないのと視覚的にわかりやすいものを使う
(setopt org-modern-star 'replace)
(setopt org-modern-replace-stars "①②③④⑤")
(setopt org-modern-hide-stars nil)
)
(with-low-priority-startup
(load-package org-modern)
(add-hook 'org-mode-hook #'org-modern-mode))
(when my:org-roam-directory
(defun my:org-agenda-files-update (&rest _)
"Update the value of `org-agenda-files'."
(setq org-agenda-files (my:org-roam-project-files))
(add-to-list 'org-agenda-files (expand-file-name "inbox.org" my:org-roam-directory)))
(defun my:org-agenda-category (&optional len)
"Get category of item at point for agenda.
Category is defined by one of the following items:
- CATEGORY property
- TITLE keyword
- TITLE property
- filename without directory and extension
When LEN is a number, resulting string is padded right with
spaces and then truncated with ... on the right if result is
longer than LEN.
Usage example:
(setq org-agenda-prefix-format
'((agenda . \" %(my:org-agenda-category) %?-12t %12s\")))
Refer to `org-agenda-prefix-format' for more information."
(let* ((file-name (when buffer-file-name
(file-name-sans-extension
(file-name-nondirectory buffer-file-name))))
(title (my:org-get-keyword "title"))
(category (org-get-category))
(result
(or (if (and
title
(string-equal category file-name))
title
category)
"")))
(if (numberp len)
(s-truncate len (s-pad-right len " " result))
result)))
(with-eval-after-load 'org-agenda
;; Agendaで使える拡張コマンド
(setopt org-agenda-custom-commands '((" " "Agenda"
((tags
"REFILE"
((org-agenda-overriding-header "To refile")
(org-tags-match-list-sublevels nil)))
(tags
"PROJECT"
((org-agenda-overriding-header "To project")
(org-tags-match-list-sublevels nil)))))))
;; 現時点を示す文字列
(setopt org-agenda-current-time-string " now")
;; 時間をくぎる文字列
(setopt org-agenda-time-grid '((daily today require-timed)
(0700 0800 0900 01000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400)
"-"
"────────────────"))
;; それぞれのprefix
(setopt org-agenda-prefix-format '((agenda . " %i %-15(my:org-agenda-category 15)%?-12t%s")
(todo . " %i %-15(my:org-agenda-category 15) ")
(tags . " %i %-15(my:org-agenda-category 15) ")
(search . " %i %-15(my:org-agenda-category 15) ")))
;; clockreportの内容
(setopt org-agenda-clockreport-parameter-plist '(
:maxlevel 5
:block t
:tstart t
:tend t
:emphasize t
:link t
:narrow 80
:indent t
:formula nil
:level 5
:tcolumns nil
:properties ("CATEGORY")
:hidefiles t)))
(advice-add 'org-agenda :before #'my:org-agenda-files-update)
(keymap-global-set "C-c a" #'org-agenda))
(eval-when-compile
(elpaca (go-mode :ref "6f4ff9ef874d151ed8d297a80f1bf27db5d9dbf0")))
;; go.modがある場所をrootとする
(defun my:project-find-go-module (dir)
(when-let ((root (locate-dominating-file dir "go.mod")))
(cons 'go-module root)))
(cl-defmethod project-root ((project (head go-module)))
(cdr project))
(defun my:go-mode-hook-1 ()
;; そのバッファでのみ有効にする
(add-hook 'project-find-functions #'my:project-find-go-module 0 t)
(eglot-ensure))
(with-eval-after-load 'go-mode
(add-hook 'go-mode-hook #'my:go-mode-hook-1))
(with-low-priority-startup
(load-package go-mode))
Rust用のmajor mode
(defun my:find-rust-project-root (dir)
(when-let ((root (locate-dominating-file dir "Cargo.lock")))
(list 'vc 'Git root)))
(defun my:rust-mode-hook ()
(setq-local project-find-functions (list #'my:find-rust-project-root))
(eglot-ensure))
(with-eval-after-load 'rust-ts-mode
(setopt rust-ts-indent-offset 4))
(with-low-priority-startup
(add-hook 'rust-ts-mode-hook #'my:rust-mode-hook)
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode)))
(defun my:python-mode-hook-0 ()
(setq-local indent-tabs-mode nil)
(pyvenv-mode +1))
(with-low-priority-startup
(add-hook 'python-mode-hook #'my:python-mode-hook-0))
venvを利用できるようにする。実際には、その時点で利用するvenvを変更する・・・みたいなこともできるみたいだが、まぁそこまではできなくてもいいかなっていう。
(eval-when-compile
(elpaca (pyvenv :ref "31ea715f2164dd611e7fc77b26390ef3ca93509b")))
(defun my:pyvenv-activate-hook ()
"pyvenvを有効にする"
(pyvenv-activate my:virtualenv-path))
(with-low-priority-startup
(load-package pyvenv)
(add-hook 'python-mode-hook #'my:pyvenv-activate-hook))
(defun my:emacs-lisp-hooks ()
(setq-local completion-at-point-functions
(list (cape-capf-case-fold
(cape-capf-super
#'tempel-complete
#'elisp-completion-at-point)))))
(with-low-priority-startup
(add-hook 'emacs-lisp-mode-hook #'my:emacs-lisp-hooks)
)
OPAMの動作が前提なので、最初にOPAMにあるやつを読み込めるようにしておく。
(eval-and-compile
(defun my:opam-share-directory-p ()
(let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share")))))
(and opam-share (file-directory-p opam-share))))
(defun my:opam-load-path ()
(let ((opam-share (ignore-errors (car (process-lines "opam" "config" "var" "share")))))
(when (and opam-share (file-directory-p opam-share))
(expand-file-name "emacs/site-lisp" opam-share)))))
(when (my:opam-share-directory-p)
(add-to-list 'load-path (my:opam-load-path)))
caml-modeよりもこちらを利用する。
(eval-when-compile
(elpaca (tuareg :ref "1d53723e39f22ab4ab76d31f2b188a2879305092")))
(with-eval-after-load 'tuareg
;; Global tuareg setting
;; ただしインデント系統はocamlformatでフォーマットされるので、ほぼここにある設定は意味がなくなっている
(setopt tuareg-let-always-indent t)
(setopt tuareg-function-indent 0)
(setopt tuareg-match-indent 0)
(setopt tuareg-sig-struct-indent 0)
(setopt tuareg-match-patterns-aligned t)
;; use ocamllsp valid in eglot
;; https://github.com/joaotavora/eglot/issues/525
(put 'tuareg-mode 'eglot-language-id "ocaml"))
(with-low-priority-startup
(load-package tuareg)
(add-hook 'tuareg-mode-hook #'eglot-ensure))
Emacs29から組み込まれたtreesitterのmoduleを前提としたもの。
(eval-when-compile
(elpaca (ocaml-ts-mode :type git :host github :repo "dmitrig/ocaml-ts-mode"
:ref "bb8c86bd49e4e98f41e45fb0ec82e38f90bc3ee4")))
(with-eval-after-load 'ocaml-ts-mode
(defvar ocaml-ts-mode-map)
(keymap-set ocaml-ts-mode-map "C-h" #'delete-backward-char)
;; use ocamllsp valid in eglot
;; https://github.com/joaotavora/eglot/issues/525
(put 'ocaml-ts-mode 'eglot-language-id "ocaml"))
(with-low-priority-startup
(load-package ocaml-ts-mode)
(add-to-list 'auto-mode-alist '("\\.ml[ily]?\\'" . ocaml-ts-mode))
(add-to-list 'auto-mode-alist '("\\.topml\\'" . ocaml-ts-mode))
(add-hook 'ocaml-ts-mode-hook #'eglot-ensure)
)
(eval-when-compile
(elpaca (lua-mode :ref "d074e4134b1beae9ed4c9b512af741ca0d852ba3")))
(with-low-priority-startup
(load-package lua-mode))
(eval-when-compile
(elpaca (markdown-mode :ref "0cdebc833ed9b98baf9f260ed12b1e36b0ca0e89")))
(with-low-priority-startup
(load-package markdown-mode))
わかりづらいが、reStructuredText。
(with-low-priority-startup
(add-to-list 'auto-mode-alist '("\\.rst\\'" . rst-mode)))
(defun my:css-setup ()
(add-node-modules-path)
(rainbow-mode +1))
(with-eval-after-load 'css-mode
(setopt css-indent-offset 2)
(add-hook 'css-ts-mode-hook #'my:css-setup)
)
(with-low-priority-startup
(add-to-list 'auto-mode-alist '("\\.s?css\\'" . css-ts-mode)))
文字の名前やコードに対して色をつける。CSS書く場合はないと、一部の特殊な人間以外はわけわからなくなる。 rainbow-modeに対するalternativeとして開発されている。
https://github.com/DevelopmentCool2449/colorful-mode
(eval-when-compile
(elpaca (colorful-mode :ref "706472ea7f0ee2fe5719cd91df7315fe9ca86114")))
(with-eval-after-load 'colorful-mode)
(with-low-priority-startup
(load-package colorful-mode))
https://github.com/zkry/yaml-pro
treesitサポートも組み込んだ、非常に高機能なyaml用のmode。
(eval-when-compile
(elpaca (yaml-pro :ref "5f06949e92dc19dcc48dc31662b2aa958fe33726"))
(elpaca (yaml :ref "70c4fcead97e9bd6594e418c922ae769818f4245")))
(with-eval-after-load 'yaml-pro
)
(with-low-priority-startup
(load-package yaml)
(load-package yaml-pro)
(add-hook 'yaml-ts-mode-hook #'yaml-pro-ts-mode)
(add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode)))
jsxを使うときにたまに使う。
(eval-when-compile
(elpaca (web-mode :ref "005aa62d6f41fbf9bc045cac3b3b772716ee8ba7")))
(defun my:web-mode-hook-angular-service ()
"Start angular lsp server if target is component"
(when (and
(string-match-p "\.component\.html\\'" (or buffer-file-name "")))
(eglot-ensure)))
(with-eval-after-load 'web-mode
(setopt web-mode-markup-indent-offset 2)
(setopt web-mode-code-indent-offset 2))
(with-low-priority-startup
(load-package web-mode)
(add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.rt\\'" . web-mode))
(add-hook 'web-mode-hook #'my:web-mode-hook-angular-service))
ここも色々多いので、個別に記載していく。
node_modules/.binをexec-pathに追加してくれる。
(eval-when-compile
(elpaca (add-node-modules-path :ref "841e93dfed50448da66c89a977c9182bb18796a1")))
(with-eval-after-load 'add-node-modules-path
;; npm v9でbinが削除されてしまったので、npm rootを利用するように
;; https://github.com/codesuki/add-node-modules-path/issues/23
(setopt add-node-modules-path-command '("echo \"$(npm root)/.bin\"")))
(with-high-priority-startup
(load-package add-node-modules-path))
(with-eval-after-load 'js-mode
(setopt js-indent-level 2)
)
(with-low-priority-startup
(add-to-list 'auto-mode-alist '("\\.[cm]?js\\'" . js-mode)))
(with-eval-after-load 'typescript-ts-mode
(setopt typescript-ts-mode-indent-offset 2)
(defvar typescript-ts-mode-map)
;; doc commentのときなどにきちんと動くようにする
(keymap-set typescript-ts-mode-map "M-j" #'default-indent-new-line)
)
(with-low-priority-startup
(add-to-list 'auto-mode-alist '("\\.m?ts\\'" . typescript-ts-mode))
(add-to-list 'auto-mode-alist '("\\.m?tsx\\'" . typescript-ts-mode))
(add-hook 'typescript-ts-mode-hook #'add-node-modules-path)
(add-hook 'typescript-ts-mode-hook #'eglot-ensure))
(eval-when-compile
(elpaca (terraform-mode :ref "a645c32a8f0f0d04034262ae5fea330d5c7a33c6"))
(elpaca (hcl-mode :ref "37f2cb1bf6fb51fbf99d4fac256298fcd6d1dd24")))
(with-low-priority-startup
(load-package hcl-mode)
(load-package terraform-mode))
(eval-when-compile
(elpaca (plantuml-mode :ref "ea45a13707abd2a70df183f1aec6447197fc9ccc")))
(with-eval-after-load 'plantuml-mode
(defvar plantuml-output-type)
(setq plantuml-output-type "png")
(setopt plantuml-jar-args '("-charset UTF-8"))
(setopt plantuml-default-exec-mode 'jar)
(let ((plantuml-jar-file (expand-file-name (locate-user-emacs-file "plantuml.jar"))))
(setopt plantuml-jar-path plantuml-jar-file)
(unless (file-exists-p plantuml-jar-file)
(call-process "curl" nil nil t "-L" "-o" plantuml-jar-file
"https://sourceforge.net/projects/plantuml/files/plantuml.jar/download")))
)
(with-low-priority-startup
(load-package plantuml-mode))
;; protobuf-modeが要求しているのでここで追加している
(eval-when-compile
(elpaca (gtags-mode :ref "2f553d0c41c470c5d2ee4210267161333969e080"))
(elpaca (protobuf-mode :type git :host github :repo "protocolbuffers/protobuf"
:ref "54d8f03974c108ef8fd0f26568cd9eb086165568")))
(defconst my:protobuf-style
'((c-basic-offset . 2)
(indent-tabs-mode . nil)))
(defun my:protobuf-mode-hook ()
(c-add-style "my-protobuf-style" my:protobuf-style))
(with-low-priority-startup
(load-package gtags-mode)
(load-package protobuf-mode)
(add-hook 'protobuf-mode-hook #'my:protobuf-mode-hook))
(eval-when-compile
(elpaca (fish-mode :ref "2526b1803b58cf145bc70ff6ce2adb3f6c246f89")))
(with-low-priority-startup
;; emacs-fish is repository name of recipe
(load-package fish-mode))
(with-eval-after-load 'text-mode
;; emacs 30.1以降で追加されるオプションで、これがあるとcompleption-at-point-functionsが上書きされてしまうので、
;; 一旦切る。これはorg modeとかでも影響する。
(setopt text-mode-ispell-word-completion nil))
Nixを活用するためのmode。
(eval-when-compile
(elpaca (nix-mode :ref "719feb7868fb567ecfe5578f6119892c771ac5e5")))
(with-eval-after-load 'nix-mode
)
(with-low-priority-startup
(load-package nix-mode)
(add-hook 'nix-mode-hook #'eglot-ensure))
https://github.com/casey/just makeの大体として利用されるjustのsyntax highlighting。
(eval-when-compile
(elpaca (just-mode :rev "4c0df4cc4b8798f1a7e99fb78b79c4bf7eec12c1")))
(with-eval-after-load 'just-mode
)
(with-low-priority-startup
(load-package just-mode))
(with-eval-after-load 'eldoc
;; idle時にdelayをかけない
(setopt eldoc-idle-delay 0)
;; echo areaに複数行表示を有効にする
(setopt eldoc-echo-area-use-multiline-p nil)
;; bufferを基本的に利用する
(setopt eldoc-echo-area-prefer-doc-buffer t)
)
(add-hook 'emacs-lisp-mode-hook #'eldoc-mode)
(add-hook 'lisp-interaction-mode-hook #'eldoc-mode)
(add-hook 'ielm-mode-hook #'eldoc-mode)
(defun my:c-mode-hook ()
;; compile-windowの設定
(defvar compilation-buffer-name)
(setq compilation-buffer-name "*compilation*")
(setq compilation-scroll-output t)
(setq compilation-read-command t)
(setq compilation-ask-about-save nil)
(setq compilation-window-height 10)
(setq compile-command "make")
(defvar c-mode-base-map)
;; cc-mode内で定義されるキーバインド
(keymap-set c-mode-base-map "C-c C-c" 'comment-region)
(keymap-set c-mode-base-map "C-c C" 'my-c++-cast)
(keymap-set c-mode-base-map "C-c C-M-c" 'uncomment-region)
(keymap-set c-mode-base-map "C-c e" 'c-macro-expand)
(keymap-set c-mode-base-map "C-c c" 'my-compile)
(keymap-set c-mode-base-map "C-c M-c" 'compilation-close)
(keymap-set c-mode-base-map "C-c g" 'gdb)
(keymap-set c-mode-base-map "C-c t" 'toggle-source)
(keymap-set c-mode-base-map "C-c C-d" 'c-down-conditional)
;; cc-modeに入る時に自動的にgtags-modeにする
(gtags-mode +1))
(with-low-priority-startup
(add-to-list 'auto-mode-alist '("\\.h\\'" . c++-mode))
(add-hook 'c-mode-common-hook #'my:c-mode-hook)
)
https://github.com/abo-abo/ace-window ウィンドウ間を1キーで移動できるようにするための拡張。
(eval-when-compile
(elpaca (ace-window :ref "77115afc1b0b9f633084cf7479c767988106c196")))
(with-eval-after-load 'posframe
(ace-window-posframe-mode +1))
(with-eval-after-load 'ace-window
(setopt aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
(with-low-priority-startup
(load-package ace-window))
https://github.com/minad/tempel tempoに似たような構文を持つ、シンプルなテンプレートエンジン。corfuなどと効果的に組み合わせることができるようなキーバインドを提供している。
(eval-when-compile
(elpaca (tempel :type git :host github :repo "minad/tempel" :branch "main"
:ref "317c0e41d542721db11a7a8a1c6b78762959259b")))
(defvar tempel-map)
(with-eval-after-load 'tempel
(keymap-set tempel-map "C-." #'tempel-next)
(keymap-set tempel-map "C-," #'tempel-previous))
(with-low-priority-startup
(load-package tempel)
;; tempel-completeがautoload ではないので、明示的にautoload にする
(autoload 'tempel-complete "tempel")
(autoload 'tempel-next "tempel")
(autoload 'tempel-previous "tempel"))
symbolをハイライトするfaceを提供する。lsp-modeとかと見事に競合するので、lsp-modeを利用する場合はオフにするのを推奨。
(eval-when-compile
(elpaca (symbol-overlay :ref "de215fff392c916ffab01950fcb6daf6fd18be4f")))
(with-eval-after-load 'symbol-overlay
(set-face-attribute 'symbol-overlay-default-face nil :inherit 'highlight :underline t)
)
(with-low-priority-startup
(load-package symbol-overlay)
(add-hook 'prog-mode-hook #'symbol-overlay-mode))
よりシンプルなbeacon https://protesilaos.com/emacs/pulsar
(eval-when-compile
(elpaca (pulsar :type git :host github :repo "protesilaos/pulsar"
:ref "c3d2205dc58bf55e58e58544c39495f1fe64a181")))
(with-eval-after-load 'pulsar
(setopt pulsar-face 'pulsar-magenta)
)
(defun my:disable-pulsar-mode ()
"pulsar-modeを明示的に無効化する"
(pulsar-mode -1))
(with-low-priority-startup
(load-package pulsar)
(pulsar-global-mode +1)
;; eldocが利用するspecial-modeでは意味がないので無効化しておく
(add-hook 'special-mode-hook #'my:disable-pulsar-mode))
(eval-when-compile
(elpaca (imenu-list :ref "76f2335ee6f2f066d87fe4e4729219d70c9bc70d")))
(with-eval-after-load 'imenu-list
(setopt imenu-list-size 0.25)
(setopt imenu-list-auto-resize nil)
(setopt imenu-list-focus-after-activation t)
)
(with-low-priority-startup
(load-package imenu-list))
(eval-when-compile
(elpaca (which-key :ref "1e89fa000e9ba9549f15ef57abccd118d5f2fe1a")))
(with-eval-after-load 'which-key
(setopt which-key-max-description-length 40)
(setopt which-key-use-C-h-commands t)
)
(with-low-priority-startup
(load-package emacs-which-key)
(require 'which-key))
https://github.com/AmaiKinono/puni smartparensと同じような、括弧をうまく扱うためのpackage.
(eval-when-compile
(elpaca (puni :ref "72e091ef30e0c9299dbcd0bc4669ab9bb8fb6e47")))
(with-eval-after-load 'puni)
(with-high-priority-startup
(load-package puni)
;; org-mode/dired-mode/vterm-mode ではあまり意味がないので無効化する
(add-hook 'org-mode-hook #'puni-disable-puni-mode)
(add-hook 'vterm-mode-hook #'puni-disable-puni-mode)
(add-hook 'dired-mode-hook #'puni-disable-puni-mode)
(puni-global-mode +1))
https://github.com/dgutov/diff-hl git-gutter系統をよりシンプルにしたもの。
(eval-when-compile
(elpaca (diff-hl :ref "b80ff9b4a772f7ea000e86fbf88175104ddf9557")))
(with-eval-after-load 'diff-hl
;; do not display border on fringe
(setopt diff-hl-draw-borders nil)
;; 非同期で更新する
(setopt diff-hl-update-async t))
(with-low-priority-startup
(load-package diff-hl)
(add-hook 'prog-mode-hook #'diff-hl-mode)
(add-hook 'text-mode-hook #'diff-hl-mode)
(add-hook 'prog-mode-hook #'diff-hl-flydiff-mode)
(add-hook 'text-mode-hook #'diff-hl-flydiff-mode)
(add-hook 'magit-pre-refresh-hook #'diff-hl-magit-pre-refresh)
(add-hook 'magit-post-refresh-hook #'diff-hl-magit-post-refresh)
)
(with-eval-after-load 'flymake
(keymap-global-set "<f2>" #'flymake-goto-next-error)
(keymap-global-set "S-<f2>" #'flymake-goto-prev-error)
)
flymakeの設定を色々一般化して作製しやすくしたpackage。collectionということなので、色々なpackageも存在している。
(eval-when-compile
(elpaca (flymake-collection :ref "ecc15c74630fa75e7792aa23cec79ea4afc28cc2")))
(with-eval-after-load 'flymake-collection
)
(with-low-priority-startup
;; flymake-collection自体には必要なものがないので、使うものを個別に指定していく
(load-package flymake-collection))
https://github.com/tumashu/posframe
(when (and window-system my:use-posframe)
(eval-when-compile
(elpaca (posframe :ref "570273bcf6c21641f02ccfcc9478607728f0a2a2")))
(when (eq window-system 'x)
(setq x-gtk-resize-child-frames 'resize-mode))
(with-low-priority-startup
(load-package posframe)))
https://github.com/casouri/eldoc-box
eldocをchildframeで表示するようにしてくれる。
(eval-when-compile
(elpaca (eldoc-box :ref "d3250fccf26649f250e8678f22276f375c01aec5")))
(with-eval-after-load 'eldoc-box
;; 複数行の場合だけ表示するようにする
(setopt eldoc-box-only-multi-line t)
)
(with-low-priority-startup
(load-package eldoc-box))
昔使ってたundo-treeの別バージョン、みたいなもの。
https://github.com/casouri/vundo
(eval-when-compile
(elpaca (vundo :ref "ca590c571546eb1d38c855216db11d28135892f2")))
(with-low-priority-startup
(load-package vundo))
Emacs29から標準添付になったので、これを利用してみる。
(defvar eglot-server-programs)
(defvar eglot-mode-map)
(with-eval-after-load 'eglot
;; 補完候補を表示するときとかにあまりにでかすぎてスローダウンしているので0にしておく
(setopt eglot-events-buffer-config '(:size 0 :format full))
(add-to-list 'eglot-server-programs '(((ocaml-ts-mode :language-id)) . ("ocamllsp")))
(add-to-list 'eglot-server-programs '(nix-mode . ("nixd")))
;; eglotでもhotfuzzを利用するようにする
(add-to-list 'completion-category-overrides
'(eglot (styles hotfuzz basic)))
(declare-function eglot-rename 'eglot)
(declare-function eglot-code-actions 'eglot)
(defvar eglot-mode-map)
(keymap-set eglot-mode-map "C-c r" #'eglot-rename)
(keymap-set eglot-mode-map "C-<return>" #'eglot-code-actions)
(keymap-set eglot-mode-map "M-m" #'eldoc-box-help-at-point)
)
(defun my:enable-language-base-flymake-backend ()
"languageごとに必要なflymakeのbackendを設定する"
(cond
((or (eq major-mode 'typescript-ts-mode)
(eq major-mode 'js-ts-mode))
(add-hook 'flymake-diagnostic-functions #'flymake-collection-eslint nil t))
(t nil)))
(with-low-priority-startup
(add-hook 'eglot-managed-mode-hook #'my:enable-language-base-flymake-backend)
(add-hook 'eglot-managed-mode-hook #'eglot-booster-mode))
eglotのcommunicateにおいて、Rust製のprogramを利用することで、JSONのParseに伴う諸々の性能問題を解消しようとするpackage。
(eval-when-compile
(elpaca (eglot-booster :type git :host github :repo "jdtsmith/eglot-booster"
:ref "e19dd7ea81bada84c66e8bdd121408d9c0761fe6")))
(with-low-priority-startup
(load-package eglot-booster))
(eval-when-compile
(elpaca (aggressive-indent :ref "a437a45868f94b77362c6b913c5ee8e67b273c42")))
(with-low-priority-startup
(load-package aggressive-indent)
(add-hook 'lisp-mode-hook #'aggressive-indent-mode)
(add-hook 'emacs-lisp-mode-hook #'aggressive-indent-mode)
)
https://github.com/copilot-emacs/copilot.el GitHub Copilotを利用するための設定。agent の起動が非常に重いので、基本的には必要になったときにだけ有効化する。
(eval-when-compile
(elpaca (copilot :type git :host github :repo "copilot-emacs/copilot.el" :files ("dist" "*.el")
:ref "535ef61e82f09d744cd5b097b1fc99f08cce175c")))
(defun my:not-completion-in-region-mode-p ()
"Predicate to check if `completion-in-region-mode' is enabled."
(null completion-in-region-mode))
(defun my:insert-state-p ()
"modal editingが起動していないかどうかを返す"
(and (fboundp 'multistate-insert-state-p)
(multistate-insert-state-p)))
(defun my:indent-for-tab-command-dwim ()
"必要があればindent-for-tab-commandを呼び出す"
(interactive)
(or (and (fboundp 'copilot-accept-completion)
(copilot-accept-completion))
(indent-for-tab-command)))
(defvar copilot-mode-map)
(defvar copilot-enable-predicates)
(defvar copilot-major-mode-alist)
(with-eval-after-load 'copilot
;; 常時やってもあまり意味がないので、タイピングが続いている間はやらないようにする
(setopt copilot-idle-delay 0.5)
;; ファイルを開く度にワーニングになるのだが、実害が基本的にないので、ワーニング自体を無視しておく
(setopt copilot-indent-offset-warning-disable t)
;; evilを使っていないので、evil関連のものは抜いておき、そのかわりにmodalkaのものを入れておく
(setq copilot-enable-predicates
'(my:insert-state-p my:not-completion-in-region-mode-p copilot--buffer-changed))
;; tuaregはocamlにしてもらわないと困る
(add-to-list 'copilot-major-mode-alist '("tuareg" . "ocaml"))
(keymap-set copilot-mode-map "<tab>" #'my:indent-for-tab-command-dwim)
(keymap-set copilot-mode-map "TAB" #'my:indent-for-tab-command-dwim)
)
(with-low-priority-startup
(load-package copilot))
https://github.com/s-kostyaev/ellama/
llm.elを利用して、ollamaとのinterfaceを提供するためのpackage。
(linux!
(eval-when-compile
(elpaca (ellama :ref "74767cbd6dc582bd6ce99a83bc84d41bfad4b4ee")))
(with-eval-after-load 'ellama
(setopt ellama-language "Japanese")
(setopt ellama-provider
(make-llm-ollama
:chat-model "gemma2:9b-instruct-q4_K_S"
:embedding-model "gemma2:9b-instruct-q4_K_S"))
;; namingに利用するproviderとschemaを定義する
(setopt ellama-translation-provider
(make-llm-ollama
:chat-model "gemma2:9b-instruct-q4_K_S"
:embedding-model "gemma2:9b-instruct-q4_K_S")
)
(setopt ellama-naming-scheme 'ellama-generate-name-by-llm)
)
(with-low-priority-startup
(load-package ellama)))
volatile-highlightsの代替。標準で用意されている pulse
というpackageが使われている。
https://github.com/minad/goggles
(eval-when-compile
(elpaca (goggles :ref "41d3669d7ae7b73bd39d298e5373ece48b656ce3")))
(with-low-priority-startup
(load-package goggles)
(add-hook 'prog-mode-hook #'goggles-mode)
(add-hook 'text-mode-hook #'goggles-mode)
(setq-default goggles-pulse t)
)
https://github.com/minad/jinx
高速かつ軽量なspell checker。基本的にはispell を利用する形か。
;; macOSの場合、segfaultが発生してしまうので、一旦止めておく
(linux!
(eval-when-compile
(elpaca (jinx :type git :host github :repo "minad/jinx" :branch "main"
:ref "cd827ee199efedc8f5e094001d90206e698f91e8")))
(with-eval-after-load 'jinx
(setopt jinx-languages "en_US ja_JP")
)
(with-low-priority-startup
(load-package jinx)
(add-hook 'with-editor-mode-hook #'jinx-mode))
)
abo-abo氏が作成している、keyによって移動することを可能とするためのpackage。
(eval-when-compile
(elpaca (avy :ref "be612110cb116a38b8603df367942e2bb3d9bdbe")))
(with-low-priority-startup
(load-package avy))
https://github.com/mhayashi1120/Emacs-wgrep grepに対応するwdired的なpackage。
(eval-when-compile
(elpaca (wgrep :ref "208b9d01cfffa71037527e3a324684b3ce45ddc4")))
(with-low-priority-startup
(load-package wgrep))
https://github.com/alan-w-255/tabby.el https://github.com/TabbyML/tabby
tabbyというllama-serverをラップしているserverとつなぐためplugin。
(linux!
(eval-when-compile
(elpaca (tabby :type git
:host github
:files ("tabby.el" "node_scripts")
:repo "alan-w-255/tabby.el"
:ref "99a00416069e6e7869225b5ea1da6c3f14fe04f6")))
(with-eval-after-load 'tabby
;; multistate-insertのときだけ有効にする
(setopt tabby-enable-predicates '(multistate-insert-state-p))
;; completion-in-region-mode == corfuが有効になっているときは邪魔なだけなので表示させない
(setopt tabby-disable-display-predicates '(my:not-completion-in-region-mode-p))
;; C-jでacceptする
(keymap-set tabby-mode-map "C-j" #'tabby-accept-completion))
(with-low-priority-startup
(load-package tabby)
(add-hook 'prog-mode-hook #'tabby-mode)))
(eval-when-compile
(elpaca (diminish :ref "fbd5d846611bad828e336b25d2e131d1bc06b83d")))
(with-low-priority-startup
(load-package diminish))
https://github.com/rainstormstudio/nerd-icons.el#installing-fonts
all-the-iconsの代替とのこと。all-the-iconsはターミナルでは利用できないらしいが、これはnerd-fontsにだけ依存しているので利用できるらしい。
(eval-when-compile
(elpaca (nerd-icons :type git :host github :repo "rainstormstudio/nerd-icons.el"
:ref "c3d641d8e14bd11b5f98372da34ee5313636e363")))
(with-high-priority-startup
(load-package nerd-icons))
minibufferでの補完時などに、nerd-iconsを使ってアイコンを表示できるようにする。
https://github.com/rainstormstudio/nerd-icons-completion
(eval-when-compile
(elpaca (nerd-icons-completion :ref "426a1d7c29a04ae8e6ae9b55b0559f11a1e8b420")))
(with-low-priority-startup
(load-package nerd-icons-completion)
(add-hook 'marginalia-mode-hook #'nerd-icons-completion-marginalia-setup)
(nerd-icons-completion-mode +1)
)
diredでnerd-iconsを利用できるようにする。
https://github.com/rainstormstudio/nerd-icons-dired
(eval-when-compile
(elpaca (nerd-icons-dired :ref "c1c73488630cc1d19ce1677359f614122ae4c1b9")))
(with-low-priority-startup
(load-package nerd-icons-dired)
(add-hook 'dired-mode-hook #'nerd-icons-dired-mode))
nerd-icons
をcorfu用に利用できるようにするためのpackage。
https://github.com/LuigiPiucco/nerd-icons-corfu?tab=readme-ov-file
(eval-when-compile
(elpaca (nerd-icons-corfu :ref "7077bb76fefc15aed967476406a19dc5c2500b3c")))
(with-eval-after-load 'corfu
(setopt corfu-margin-formatters '(nerd-icons-corfu-formatter)))
(with-high-priority-startup
(load-package nerd-icons-corfu))
emojiを表示するためのpackage 。GitHubみたいな ::
で挾んだような形式や、英語圏のascii artとかを対象としている。
(eval-when-compile
(elpaca (emojify :ref "1b726412f19896abf5e4857d4c32220e33400b55")))
(with-eval-after-load 'emojify
(setopt emojify-display-style 'unicode)
(setopt emojify-emoji-style '(unicode github))
)
(with-low-priority-startup
(load-package emojify)
(global-emojify-mode +1))
(eval-when-compile
(elpaca (exec-path-from-shell :ref "72ede29a0e0467b3b433e8edbee3c79bab005884")))
(with-high-priority-startup
(load-package exec-path-from-shell)
(exec-path-from-shell-initialize)
(let ((envs '("GOROOT" "GOPATH" "PATH")))
(exec-path-from-shell-copy-envs envs)))
rg.elだとtransientとのnative compで相性が悪かったので、こっちにもどす。
(eval-when-compile
(elpaca (ripgrep :ref "b6bd5beb0c11348f1afd9486cbb451d0d2e3c45a")))
(with-eval-after-load 'ripgrep
(setopt ripgrep-arguments '("--smart-case"
"--hidden"
)))
(with-low-priority-startup
(load-package ripgrep))
(when my:mozc-helper-locate
(eval-when-compile
(elpaca (mozc :ref "7967c42e5585d0789fe6565bf366afba8b31fcbf"))
(elpaca (mozc-posframe :type git :host github :repo "derui/mozc-posframe"
:files ("mozc-posframe.el")
:ref "9adb3a258ff0457dfb556c78ddbb3b8c014ceb60")))
(defvar mozc-keymap-kana)
(defvar mozc-helper-program-name)
(with-eval-after-load 'mozc
;; ここで初期化をしておかないと動かない
(mozc-posframe-initialize)
(setq mozc-keymap-kana mozc-keymap-kana-101us)
(setopt mozc-candidate-style 'posframe)
(setq mozc-helper-program-name my:mozc-helper-locate))
(with-low-priority-startup
(load-package mozc)
(load-package mozc-posframe)))
Emacs 29からはtreesitという形でtree-sitterが組み込みで利用できるようになっている。ただしこれ、現状だと *-ts-mode
という標準モードでしか利用されていないらしく、かつそっちを利用しようとするとかなり大変なことになったりが多い。
treesit自体は魅力的なのだが、font-lockの仕組みそのものが別物ということのようなので、別物として作成しないといけない雰囲気が大分する。
(with-eval-after-load 'treesit
;; font lockで最大のレベルを利用しておく
(setopt treesit-font-lock-level 4))
(eval-when-compile
(elpaca (treesit-auto :ref "016bd286a1ba4628f833a626f8b9d497882ecdf3")))
(with-eval-after-load 'treesit-auto
;; 対象のパーサがすでにあったら自動的にインストールしてくれる
(setopt treesit-auto-install t)
)
(with-low-priority-startup
(load-package treesit-auto)
(autoload 'treesit-auto-mode "treesit-auto")
(add-hook 'prog-mode-hook #'treesit-auto-mode))
diredにfont-lockを適用していい感じにしてくれる。
(eval-when-compile
(elpaca (diredfl :ref "f6d599c30875ab4894c1deab9713ff2faea54e06")))
(with-low-priority-startup
(load-package diredfl)
(add-hook 'dired-mode-hook #'diredfl-mode))
定番のパッケージ。括弧を階層毎に色付けしてくれる。
(eval-when-compile
(elpaca (rainbow-delimiters :ref "f40ece58df8b2f0fb6c8576b527755a552a5e763")))
(with-low-priority-startup
(load-package rainbow-delimiters)
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode))
パンくずリスト。
https://github.com/joaotavora/breadcrumb
(eval-when-compile
(elpaca (breadcrumb :ref "dcb6e2e82de2432d8eb75be74c8d6215fc97a2d3")))
(with-eval-after-load 'breadcrumb)
(with-low-priority-startup
(load-package breadcrumb)
(add-hook 'prog-mode-hook #'breadcrumb-local-mode))
自作のKanzen clone。
(eval-when-compile
(elpaca (chokan :type git :host github :repo "derui/chokan"
:ref "206acbfdc9709fa0392c4ad97cc263394db4ac1a"))
(elpaca (websocket :ref "40c208eaab99999d7c1e4bea883648da24c03be3")))
(with-eval-after-load 'chokan
(chokan-websocket-setup)
)
(with-low-priority-startup
(load-package websocket)
(load-package chokan))
https://github.com/emacsorphanage/anzu
結構昔からあるVisual replace を実現するためのpackage。どっちかというと、本来はmode Line にマッチ状況を表示することが目的というかなんというか、というものだったようだ。
(eval-when-compile
(elpaca (anzu :ref "26fb50b429ee968eb944b0615dd0aed1dd66172c")))
(with-eval-after-load 'anzu
;; mode lineは自分で制御したいので、勝手にCons するのは許容しない
(setopt anzu-cons-mode-line-p nil)
)
(with-low-priority-startup
(load-package anzu)
(global-anzu-mode +1))
https://github.com/jdtsmith/indent-bars
indent-guideを表示するpackage 。他のmode と比較して、軽量かつ高速である、とのこと。
(eval-when-compile
(elpaca (indent-bars :type git :host github :repo "jdtsmith/indent-bars" :branch "main"
:ref "e28f3321a00159cff640ac2a7eb452b5a8a2e364")))
(defun my:indent-bars-mode-dwim ()
"treesitが有効な場合は `indent-bars-ts-mode' を起動する。treesitが
有効ではないmode の場合は `indent-bars-mode' を代りに起動する"
(if (and (functionp 'treesit-available-p)
(treesit-available-p))
(or (indent-bars-ts-mode +1)
(indent-bars-mode +1))
(indent-bars-mode +1)))
(with-eval-after-load 'indent-bars
(setopt indent-bars-color '(highlight :face-bg t :blend 0.2))
(setopt indent-bars-pattern " . .")
(setopt indent-bars-width-frac 0.15)
;; treesitをサポートする
(setopt indent-bars-treesit-support t)
;; treesitterにおいて、moduleという単位ではbar を表示しない
(setopt indent-bars-treesit-ignore-blank-lines-types '("module"))
)
(with-low-priority-startup
(load-package indent-bars)
(add-hook 'yaml-ts-mode-hook #'my:indent-bars-mode-dwim)
(add-hook 'rust-ts-mode-hook #'my:indent-bars-mode-dwim)
(add-hook 'typescript-ts-mode-hook #'my:indent-bars-mode-dwim)
)
https://github.com/magnars/multiple-cursors.el
複数のカーソルを同時にあつかうことができるpackage 。
(eval-when-compile
(elpaca (multiple-cursors :type git :host github :repo "magnars/multiple-cursors.el" :branch "master"
:ref "c870c18462461df19382ecd2f9374c8b902cd804")))
(with-eval-after-load 'multiple-cursors
(defvar mc/cmds-to-run-for-all nil)
(setq mc/cmds-to-run-for-all '(my:treesit-expand-region
puni-kill-active-region))
)
(defun my:normal-state-after-leave-mc ()
"multiple-cursors-modeが終ったら、normal stateに戻る"
(when (not multiple-cursors-mode)
(multistate-normal-state)
))
(with-low-priority-startup
(load-package multiple-cursors)
;; multiple-cursors-modeが終了したら、normal stateに戻る
(add-hook 'multiple-cursors-mode-hook #'my:normal-state-after-leave-mc))
https://github.com/akermu/emacs-libvterm libvtermを利用したterminal emulator package.
(eval-when-compile
(elpaca (vterm :type git :host github :repo "akermu/emacs-libvterm" :branch "master"
:ref "d9ea29fb10aed20512bd95dc5b8c1a01684044b1")))
(defvar vterm-mode-map)
(with-eval-after-load 'vterm
(keymap-set vterm-mode-map "C-o" #'window-toggle-side-windows))
(with-low-priority-startup
(load-package vterm))
(when (and my:migemo-command (executable-find my:migemo-command))
(eval-when-compile
(elpaca (migemo :ref "7d78901773da3b503e5c0d5fa14a53ad6060c97f")))
(with-eval-after-load 'migemo
(setopt migemo-command my:migemo-command)
(setopt migemo-options '("-q" "--emacs"))
(setopt migemo-dictionary my:migemo-dictionary)
(setopt migemo-user-dictionary nil)
(setopt migemo-regex-dictionary nil)
(setopt migemo-coding-system 'utf-8-unix)
;; 遅いのを防ぐためにキャッシュする。
(setopt migemo-use-pattern-alist t)
(setopt migemo-use-frequent-pattern-alist t)
(setopt migemo-pattern-alist-length 1024))
(with-low-priority-startup
(load-package migemo)
(autoload 'migemo-get-pattern "migemo")
(require 'migemo)
(migemo-init))
)
dynamic macro。key sequenceをある程度自動的に判別して、dynamicにmacroとして振る舞ってくれる。
(eval-when-compile
(elpaca (dmacro :type git :host github :repo "emacs-jp/dmacro"
:ref "3480b97aaad9e65fa03c6a9d1a0a8111be1179f8")))
(with-eval-after-load 'dmacro)
(with-low-priority-startup
(load-package dmacro)
;; customizeなのだが、modeの定義時に使われてしまうので、setqで定義しておく必要がある
(setq dmacro-key (kbd "C-t"))
(global-dmacro-mode +1))
direnvをbuffer-localに適用するためのpackage。
(eval-when-compile
(elpaca (envrc :type git
:ref "2316e004c1574234fe4d991bd75a254cdeaa83ae")))
(with-eval-after-load 'envrc)
(defun my:disable-envrc-mode ()
"envrc-modeを明示的に無効化する"
(envrc-mode -1))
(with-low-priority-startup
(load-package envrc)
(envrc-global-mode +1)
(add-hook 'special-mode-hook #'my:disable-envrc-mode))
(defvar skk-user-directory (expand-file-name "skk" user-emacs-directory))
(leaf ddskk
:straight t
:if nil
;; ddskkは (provide 'skk) されているので、skkでrequireするようにする
:commands skk-mode
:bind (("<Hangul>" . my:enable-japanese-input)
("<henkan>" . my:enable-japanese-input)
("<f13>" . my:enable-japanese-input)
("<Hangul_Hanja>" . my:disable-japanese-input)
("<muhenkan>" . my:disable-japanese-input)
("C-<f13>" . my:disable-japanese-input))
:preface
(defun my:enable-japanese-input ()
(interactive)
(set-input-method my:input-method))
(defun my:disable-japanese-input ()
(interactive)
(set-input-method nil))
:init
(setq default-input-method my:input-method
skk-init-file (expand-file-name "init-ddskk.el" user-emacs-directory))
(defun my:disable-skk-modeline-force-change (old-func &rest r)
"そのままだとmode lineのフォーマットが勝手に変わってしまって非常に面倒なことになるため、
起動する瞬間だけ該当の処理をスキップする。
"
(setq skk-status-indicator 'minor-mode)
(apply old-func r)
(setq skk-status-indicator 'left))
(advice-add #'skk-mode-invoke :around #'my:disable-skk-modeline-force-change))
(leaf ddskk-posframe
:straight t
:if nil
:global-minor-mode t)
(leaf *skk-server
:after f
:if nil
:init
(let ((server-program (expand-file-name "yaskkserv2" my:user-local-exec-path))
(dictionary-program (expand-file-name "yaskkserv2_make_dictionary" my:user-local-exec-path)))
(cond ((and my:build-skkserver
(executable-find "cargo")
(not (executable-find server-program))
(not (executable-find dictionary-program)))
(let ((base-path "/tmp/yaskkserv2"))
(unless (f-exists? base-path)
(call-process "git" nil nil t "clone" "https://github.com/wachikun/yaskkserv2" "/tmp/yaskkserv2"))
(call-process "cargo" nil nil t "build" "--release" "--manifest-path" (expand-file-name "Cargo.toml" base-path))
(unless (f-exists? server-program)
(f-copy (expand-file-name "target/release/yaskkserv2" base-path) server-program))
(unless (f-exists? dictionary-program)
(f-copy (expand-file-name "target/release/yaskkserv2_make_dictionary" base-path) dictionary-program))
))
(t
(let* ((target (cond ((eq window-system 'ns) "apple-darwin")
(t "uknown-linux-gnu")))
(path (format "https://github.com/wachikun/yaskkserv2/releases/download/%s/yaskkserv2-%s-x86_64-%s.tar.gz" my:yaskkserv2-version my:yaskkserv2-version target)))
(call-process "curl" nil nil t "-L" path "-o" "/tmp/yaskkserv2.tar.gz")
(call-process "tar" nil nil t "-zxvf" "/tmp/yaskkserv2.tar.gz" "-C" my:user-local-exec-path "--strip-components" "1"))))))
tab barを有効化する。
;; faceなどの定義まで行うために先頭で有効化しておく。
(tab-bar-mode +1)
(defface my:tab-bar-separator-face `((t (
:weight light
:height 1.2
:background ,(face-attribute 'tab-bar-tab :background)
:box (:line-width (12 . 8) :color nil :style flat-button)
:inherit tab-bar
)))
"My tab separator face")
(defface my:tab-bar-inactive-separator-face `((t (:inherit my:tab-bar-separator-face)))
"My tab separator face for inactive tab")
;; modus-themeが適用されることを前提とした動作になっているので、modus-themesを前提にする
(with-eval-after-load 'modus-themes
(defvar my:tab-bar-format-function #'tab-bar-tab-name-format-default
"formatting function to display tab name")
(defvar my:tab-bar-face-function #'tab-bar-tab-face-default
"Get face by tab")
(defun my:tab-suffix ()
"Empty suffix of tab."
" ")
;; tab-barのstyleをmodusに適合するようにする
(setq modus-themes-common-palette-overrides
'((bg-tab-bar bg-active)
(bg-tab-current bg-main)
(bg-tab-other bg-active)))
(with-eval-after-load 'tab-bar
(setopt tab-bar-close-button-show nil)
(setopt tab-bar-auto-width nil)
;; modus-themeに適合させつつ、modern-tab-barライクなstyleにする
(set-face-attribute 'tab-bar nil
:box '(:line-width (12 . 8) :color nil :style flat-button)
:weight 'light)
(defun my:tab-name-format-function (name tab i)
"Tab nameの周辺にSpaceをいれるためのfunction"
(let* ((separator-face (if (eq (car tab) 'current-tab)
'my:tab-bar-separator-face
'my:tab-bar-inactive-separator-face)))
(concat
(propertize " "
'face separator-face)
name
(propertize " "
'face separator-face)
)))
(setopt tab-bar-format '(tab-bar-format-tabs my:tab-suffix))
;; 末尾に追加することで、セパレーターを調整する
(add-to-list 'tab-bar-tab-name-format-functions #'my:tab-name-format-function t)
(setopt tab-bar-separator ""))
)
(defun my:tab-bar-face-change ()
"tab-barで利用するfaceをloadしたthemeに合致させる"
(set-face-attribute 'my:tab-bar-inactive-separator-face nil
:background (modus-themes-get-color-value 'bg-button-active)
:box `(:line-width (12 . 8) :color ,(modus-themes-get-color-value 'bg-button-active) :style flat-button)
)
(set-face-attribute 'my:tab-bar-separator-face nil
:background (face-attribute 'tab-bar-tab :background)
:box `(:line-width (12 . 8) :color nil :style flat-button)
)
)
(add-hook 'modus-themes-post-load-hook #'my:tab-bar-face-change)
https://github.com/alphapapa/activities.el
KDE PlasmaにあるActivityをEmacsに持ってきたもの、という感じであるらしい。tab-bar modeとのintegrationが追加されているため、そのままで利用できる。
(eval-when-compile
(elpaca (activities :rev "a341ae21c4ef66879fdfbc1260b9094b5bb60e9f")))
(with-eval-after-load 'activities
;; 終了するときには全体を保存するようにする。
(add-hook 'kill-emacs-hook #'activities-save-all)
)
(with-low-priority-startup
(load-package activities)
(activities-tabs-mode +1))
(defun my:enable-japanese-input ()
(interactive)
(set-input-method my:input-method))
(defun my:disable-japanese-input ()
(interactive)
(set-input-method nil))
(setq default-input-method my:input-method)
(with-low-priority-startup
(seq-each (lambda (v)
(keymap-global-set (car v) (cadr v)))
'(
("<Hangul>" my:enable-japanese-input)
("<henkan>" my:enable-japanese-input)
("<f13>" my:enable-japanese-input)
("<Hangul_Hanja>" my:disable-japanese-input)
("<muhenkan>" my:disable-japanese-input)
("C-<f13>" my:disable-japanese-input)
)))
(eval-when-compile
(elpaca (dashboard :ref "3852301f9c6f3104d9cc98389612b5ef3452a7de")))
(with-eval-after-load 'dashboard
(declare-function dashboard-modify-heading-icons 'dashboard)
(dashboard-modify-heading-icons '((recents . "nf-oct-file")
(projects . "nf-oct-project")
(agenda . "nf-oct-calendar")))
(setopt dashboard-display-icons-p t)
(setopt dashboard-set-heading-icons t)
(setopt dashboard-set-file-icons t)
(setopt dashboard-icon-type 'nerd-icons)
(setopt dashboard-vertically-center-content t)
(setopt dashboard-startup-banner 'ascii)
(setopt dashboard-set-navigator t)
(setopt dashboard-set-init-info t)
(setopt dashboard-items '((recents . 15)
(projects . 5)
(agenda . 5)))
(setopt dashboard-banner-ascii "
____
| _ \\ ___ _ __ _ _ ___ _ __ ___ __ _ ___ ___
| | | |/ _ \\ '__| | | |/ _ \\ '_ ` _ \\ / _` |/ __/ __|
| |_| | __/ | | |_| | __/ | | | | | (_| | (__\\__ \\
|____/ \\___|_| \\__,_|\\___|_| |_| |_|\\__,_|\\___|___/
")
)
(with-eval-after-load 'diminish
(diminish 'dashboard-mode))
(with-low-priority-startup
(load-package dashboard))
最後にmagic file を有効化する。
(with-low-priority-startup
(setq file-name-handler-alist my-saved-file-name-handler-alist))
#x10000000
= 256MiB
を閾値としておく。これはLSPの対策のため。
(with-low-priority-startup
(setq gc-cons-threshold #x10000000)
(setq gc-cons-percentage 0.5)
(setq garbage-collection-messages t)
;; font cacheのcompact化を抑制する
(setq inhibit-compacting-font-caches t))
(provide 'init)