Skip to content

Instantly share code, notes, and snippets.

@include-yy
Last active December 14, 2024 13:57
Show Gist options
  • Save include-yy/e70dcbfc1a80403814d0b7a7357971d9 to your computer and use it in GitHub Desktop.
Save include-yy/e70dcbfc1a80403814d0b7a7357971d9 to your computer and use it in GitHub Desktop.
include-yy's emacs config file
;;; init.el --- include-yy's emacs config -*- lexical-binding:t;no-byte-compile:t; -*-
;; Copyright (C) 2023 include-yy <yy@egh0bww1.com>
;; Author: include-yy <yy@egh0bww1.com>
;; Created: 17 Jun 2023
;; Version: 0.1
;; Keywords: config
;; URL: https://gist.github.com/include-yy/e70dcbfc1a80403814d0b7a7357971d9
;; Everyone is permitted to copy and distribute verbatim or modified
;; copies of this license document, and changing it is allowed as long
;; as the name is changed.
;; DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
;; TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
;; 0. You just DO WHAT THE FUCK YOU WANT TO.
;;; Commentary:
;; 建议创建 early-init.el 并添加如下代码:
;; ;; -*- lexical-binding: t; -*-
;; (setopt package-user-dir
;; (expand-file-name
;; (format "elpa-%s"
;; emacs-major-version)
;; user-emacs-directory))
;; 请将本文件放在 ~/.emacs.d 目录,并删除可能存在的 ~/.emacs 文件
;; ;;@1 基础设置, ;;@2 内置包的设置, ;;@3 外部包的设置
;; ;;@4 一些零散的配置, ;;@5 详细的语言特定配置
;; ;;@@NAME 某包的配置, ;;@@ 一些无名配置, ;;te 需要手动配置的项
;; 一些可供参考的配置
;; https://github.com/purcell/emacs.d
;; https://github.com/manateelazycat/lazycat-emacs
;; https://github.com/redguardtoo/emacs.d
;; https://github.com/seagle0128/.emacs.d
;; https://github.com/lynnux/.emacs.d
;;; Code:
;;@1 基础配置
;;@@ emacs 版本必须大于等于 29.1
(when (version< emacs-version "29.1")
(error "init.el: emacs version must >= 29.1"))
;;@@ 调整 emacs 的默认 GC 大小
;; https://github.com/purcell/emacs.d/blob/master/init.el#L27
(let ((normal-gc-cons-threshold (* 20 1024 1024))
(init-gc-cons-threshold (* 128 1024 1024)))
(setq gc-cons-threshold init-gc-cons-threshold)
(add-hook 'emacs-startup-hook
(lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))
;;@@ 定义用户的自定义 group,可以保存一些特定于机器的选项
;; 或者其他的一些选项。配置文件中出现的 option 默认属于该组,无需指定 `:group'
(defgroup yy nil
"配置文件中的选项,可用来保存一些特定于机器的路径或选项"
:group 'emacs)
(defmacro yy/defcustom (symbol value doc &rest args)
"保证使用 `yy/defcustom' 定义的 option 位于 `yy' 组内"
(declare (doc-string 3) (debug (name body))
(indent defun))
(let ((args (if (plist-get args :group) args
(cons :group (cons ''yy args)))))
`(defcustom ,symbol ,value ,doc ,@args)))
(defun yy/add-to-group (sym)
"添加某一 option 到 yy group 中,方便显示"
(custom-add-to-group 'yy sym 'custom-variable))
(defun yy/customize ()
(interactive)
(customize-group 'yy))
;;@@ 额外的配置文件 `yy-local.el'
(when (file-exists-p "~/.emacs.d/yy-local.el")
(load "~/.emacs.d/yy-local.el"))
;;@@ 设置 custom.el 的位置
(setopt custom-file "~/.emacs.d/custom.el")
(load custom-file t)
;;@@ 查看配置中需要手动配置的部分
(defun yy/occur-te ()
"使用 `occur' 列出配置文件中需要手动配置的位置 ;;te"
(interactive)
(with-current-buffer (get-file-buffer "~/.emacs.d/init.el")
(occur "^ *;;te")))
;;@@ 设置 changelog file 相关的一些选项
(setopt add-log-full-name "include-yy")
(setopt add-log-mailing-address "yy@egh0bww1.com")
(setopt add-log-time-zone-rule t) ; 使用 UTC+0 时间
;;@@ 启动时不显示 GNU Emacs startup 页面
(setopt inhibit-startup-screen t)
(setopt initial-scratch-message (format ";;Emacs %s" emacs-version))
;;@@ 关闭 C-g, 边界移动命令响铃
(setopt ring-bell-function 'ignore)
;;@@ 不使用对话框
(setopt use-dialog-box nil)
;;@@ 基础补全设置
;; 按下 TAB 时进行补全,可设置为 'complete
;; 使用补全框架则不用设置
;; (setopt tab-always-indent 'complete)
;; 让 C-h f, C-h v 在选词阶段提供更多信息,更好看
(setopt completions-detailed t)
;; Emacs 29 提供了许多不错的补全改进
;; 但是,我们现在有超级好用的 vectico 了,不过加上也不费事
;; https://www.scss.tcd.ie/~sulimanm/posts/default-emacs-completion.html
;; https://robbmann.io/posts/emacs-29-completions/
(setopt completions-format 'one-column) ; 在补全 buffer 中单列显示候选词
(setopt completions-header-format nil) ; 补全 buffer 中不显示汇总信息
(setopt completions-max-height 20) ; 补全 buffer 限高为 20 行
(setopt completion-auto-select nil) ; 触发补全时不移动焦点到补全 buffer
;; 也许可以考虑试试 Protesilaos Stavrou 的时尚小垃圾 -- mct.el
;; https://protesilaos.com/emacs/mct
;;@@ 总是使用 y-or-n-p,可以少打字
;; 也可以设置 `use-short-answers' 为非空值
;; (defalias 'yes-or-no-p 'y-or-n-p)
(setopt use-short-answers t)
;;@@ 关闭一些窗口元素
(tool-bar-mode -1) ; 关闭工具栏
(scroll-bar-mode -1) ; 关闭滚动条
(menu-bar-mode -1) ; 关闭菜单栏
(blink-cursor-mode -1) ; 光标不闪烁
;;@@ backup 与 auto-save 设置
;; 不让 Emacs 生成 backup file,也就是 ~ 结尾的临时文件
(setopt make-backup-files nil)
;; 不进行 auto-save
(setopt auto-save-default nil)
;; 不创建 lock files
;; https://www.gnu.org/software/emacs/manual/html_node/elisp/File-Locks.html
(setopt create-lockfiles nil)
;;@@ 设置默认的 major-mode 为 `text-mode'
(setq-default major-mode 'text-mode)
;;@@ 从子进程读取输出时的最大单次 thunk 读取字节数量,设置为 1MB
;; (setq process-adaptive-read-buffering nil)
(setq read-process-output-max (* 1024 1024))
;;@@ 运行 `run-lisp' 时使用的 Lisp 程序名
;; sbcl 官网:http://www.sbcl.org/index.html
(setopt inferior-lisp-program "sbcl")
;;@@ 单次滚动设置为 1 行
(setopt scroll-step 1)
(setopt scroll-conservatively 10000)
;;@@ 中文智能换行
(setopt word-wrap-by-category t)
;;@@ 时间格式设置为 "C"
(setq system-time-locale "C")
;;@@ 添加一些全局绑定
(bind-keys
;; 正则向前和向后搜索的快捷键
;; 使用 consult 爽一点
;;("C-c s" . isearch-forward-regexp)
;;("C-c r" . isearch-backward-regexp)
;; 不询问直接杀死 buffer
("C-x k" . kill-current-buffer)
;; 互换 C-w 和 M-w
("M-w" . kill-region)
("C-w" . kill-ring-save)
;; 防止误触
("C-c <f12>" . save-buffers-kill-emacs)
;; 中文输入法下的处理,不用切换输入法了
("C-x 【" . backward-page)
("C-x 】" . forward-page)
;; 日文输入法下的处理
("C-x 「" . backward-page)
("C-x 」" . forward-page)
("M-x" . execute-extended-command)
("C-x b" . switch-to-buffer)
("M-w" . kill-region))
;;@@ 倾向使用 utf-8 作为 buffer 默认编码
(prefer-coding-system 'utf-8)
;;@@ 在关闭关联进程的 buffer 时不提示是否关闭
(setq kill-buffer-query-functions
(remq 'process-kill-buffer-query-function
kill-buffer-query-functions))
;;@@ 长行优化,以及一些显示效果优化
;; https://emacs.stackexchange.com/questions/598/how-do-i-prevent-extremely-long-lines-making-emacs-slow
(setq-default bidi-display-reordering 'left-to-right)
(setq-default bidi-paragraph-direction 'left-to-right)
(setq-default bidi-inhibit-bpa t)
(setq-default cursor-in-non-selected-windows nil)
(setopt fast-but-imprecise-scrolling t)
;;@@ kill 时若与 kill-ring 内最后内容重复则不添加入 kill-ring
(setopt kill-do-not-save-duplicates t)
;;@@ 将大文件警告提升至大约 100MB
(setopt large-file-warning-threshold (* 100 1024 1024))
;;@@ 禁止光标移动到 minibuffer 的 prompt
(setopt minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
;;@@ 默认 80 的 fill-column
(setopt fill-column 80)
;;@@ 解锁一些被禁用的命令
(put 'list-timers 'disabled nil)
(put 'set-goal-column 'disabled nil)
(put 'erase-buffer 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(put 'narrow-to-page 'disabled nil)
;;@@ Windows 相关配置
(when (eq system-type 'windows-nt)
;; 为 Windows 添加 Super 和 Hyper 按键
;;(setq w32-pass-lwindow-to-system nil)
;;(setq w32-lwindow-modifier 'hyper)
;;(setq w32-pass-rwindow-to-system nil)
;;(setq w32-rwindow-modifier 'super)
(setq w32-apps-modifier 'hyper)
;; 默认的 4KB 管道 buffer 太小了点,给到 64KB
(setq w32-pipe-buffer-size (* 64 1024))
;; 在 Windows 下删除文件时默认移动到垃圾箱/回收站
(setopt delete-by-moving-to-trash t)
;; 在 Windows 上没必要搜索两遍 auto-mode-alist,
;; 因为 Windows 文件系统忽略大小写
(setopt auto-mode-case-fold nil)
;; tooltip-mode 在 Windows 下似乎有点卡
(tooltip-mode -1))
;;@@ 在 Windows 下的 `grep', `find' 和 `xargs' 等命令行工具的位置
;;te 需要 msys2,指定 msys2 的根目录位置
;; 设定后执行 `when' 处的代码即可
(yy/defcustom yy/w32-msys-root-path nil
"Windows 系统下的 msys2 /usr/bin 路径"
:type '(choice (const nil)
(string :tag "path")))
(when (and (eq system-type 'windows-nt)
yy/w32-msys-root-path)
(mapc (lambda (v)
(let ((sym (car v))
(val (cdr v)))
(set sym (file-name-concat
yy/w32-msys-root-path "usr/bin" val))))
'((grep-program . "grep.exe")
(find-program . "find.exe")
(xargs-program . "xargs.exe")
(diff-command . "diff.exe"))))
;; 启动 MSYS2 UCRT64 SHELL 的函数
(defun yy/ucrt-shell ()
(interactive)
(if (not yy/w32-msys-root-path)
(user-error "Path of MSYS2 root is unknown")
(let* ((env (if (not current-prefix-arg) ; 使用 C-u 选择不同的 MSYS2 环境
"-ucrt64"
(completing-read
"Select a MSYS2 environment"
'("-msys" "-ucrt64"
"-clang64" "-mingw64"))))
(shell (file-name-concat yy/w32-msys-root-path "msys2_shell.cmd"))
(args (list "-defterm" "-here" "-no-start" env "-i")))
(dlet ((explicit-shell-file-name shell)
(explicit-msys2_shell.cmd-args args))
(shell "*msys2-shell*")))))
;;@@ Windows 下的编码设定
;; 建议使用代码页 65001
;;te Windows 10: region setting -> Language -> Administrative language settings
;; -> Change systel locale -> Beta: Use Unicode UTF-8 for worldwide language support
(when (eq system-type 'windows-nt)
(let* ((codepage (w32-get-console-codepage))
(coding (pcase codepage
(65001 'utf-8-dos)
(936 'gbk-dos)
(_ nil))))
(when coding
(setq process-coding-system-alist
`(("[pP][lL][iI][nN][kK]" . #1=(,coding . ,coding))
("[cC][mM][dD][pP][rR][oO][xX][yY]" . #1#)))
(setq default-process-coding-system
`(,coding . ,coding)))))
;;@@ LINUX 额外的执行路径,(如果没有添加到 $PATH 的话)
;; 添加 ~/.local/bin 到 exe-path
(when (eq system-type 'gnu/linux)
(add-to-list 'exec-path (expand-file-name "~/.local/bin")))
;;@@ 豆腐块消除计划
;;te 字体设置,使 C-h h 可全部正常显示
;; https://qiita.com/styzo/items/28d5d994a293fa704476
(yy/defcustom yy/all-font-available nil
"是否集齐了可以显示 C-h h 中所有语言的字体"
:type 'boolean)
(when yy/all-font-available
(add-hook 'after-init-hook
(lambda ()
(run-with-idle-timer
1 nil
'yy/all-font-init))))
(defun yy/all-font-init ()
(set-fontset-font t 'adlam (font-spec :family "Noto Sans Adlam"))
(set-fontset-font t 'ethiopic (font-spec :family "Noto Serif Ethiopic")) ;; amharic
(set-fontset-font t 'arabic (font-spec :family "Noto Sans Arabic"))
(set-fontset-font t 'balinese (font-spec :family "Noto Serif Balinese"))
(set-fontset-font t 'batak (font-spec :family "Noto Sans Batak"))
(set-fontset-font t 'bengali (font-spec :family "Nirmala UI"))
(set-fontset-font t 'brahmi (font-spec :family "Noto Sans Brahmi"))
(set-fontset-font t 'braille "Symbola")
(set-fontset-font t 'buginese (font-spec :family "Noto Sans Buginese"))
(set-fontset-font t 'burmese (font-spec :family "Noto Sans Myanmar"))
(set-fontset-font t 'cham (font-spec :family "Noto Sans Cham"))
(set-fontset-font t 'cherokee (font-spec :family "Noto Sans Cherokee"))
(set-fontset-font t 'coptic (font-spec :family "Noto Sans coptic"))
(set-fontset-font t 'canadian-aboriginal
(font-spec :family "Noto Sans Canadian Aboriginal")) ;; cree
(set-fontset-font t 'devanagari (font-spec :family "Nirmala UI"))
(set-fontset-font t 'egyptian (font-spec :family "Noto Sans Egyptian Hieroglyphs"))
(set-fontset-font t 'emoji (font-spec :family "Symbola"))
(set-fontset-font t 'georgian (font-spec :family "Noto Serif Georgian"))
(set-fontset-font t 'gothic (font-spec :family "Noto Sans Gothic"))
(set-fontset-font t 'grantha (font-spec :family "Noto Serif Grantha"))
(set-fontset-font t 'gujarati (font-spec :family "Nirmala UI"))
(set-fontset-font t 'gurmukhi (font-spec :family "Nirmala UI"))
(set-fontset-font t 'hanifi-rohingya (font-spec :family "Noto Sans Hanifi Rohingya"))
(set-fontset-font t 'hanunoo (font-spec :family "Noto Sans Hanunoo"))
(set-fontset-font t 'hebrew "Noto Sans Hebrew")
(set-fontset-font t 'javanese (font-spec :family "Noto Sans Javanese"))
(set-fontset-font t 'kaithi (font-spec :family "Noto Sans Kaithi"))
(set-fontset-font t 'kannada (font-spec :family "Noto Serif Kannada"))
(set-fontset-font t 'kharoshthi (font-spec :family "Noto Sans Kharoshthi"))
(set-fontset-font t 'khmer (font-spec :family "Noto Sans Khmer"))
(set-fontset-font t 'lao (font-spec :family "Noto Sans Lao"))
(set-fontset-font t 'lepcha (font-spec :family "Noto Sans Lepcha"))
(set-fontset-font t 'limbu (font-spec :family "Noto Sans Limbu"))
(set-fontset-font t 'makasar (font-spec :family "Noto Serif Makasar"))
(set-fontset-font t 'malayalam (font-spec :family "Noto Sans Malayalam"))
(set-fontset-font t 'thaana (font-spec :family "Noto Sans Thaana")) ;; Maldivian
(set-fontset-font t 'meetei-mayek (font-spec :family "Noto Sans Meetei Mayek"))
(set-fontset-font t 'mende-kikakui (font-spec :family "Noto Sans Mende Kikakui"))
(set-fontset-font t 'modi (font-spec :family "Noto Sans Modi"))
(set-fontset-font t 'mongolian (font-spec :family "Noto Sans Mongolian"))
(set-fontset-font t 'tai-tham (font-spec :family "Noto Sans Tai Tham")) ; Northern Thai
(set-fontset-font t 'oriya (font-spec :family "Noto Sans Oriya")) ;; odia
(set-fontset-font t 'rejang (font-spec :family "Noto Sans Rejang"))
(set-fontset-font t 'sharada (font-spec :family "Noto Sans Sharada"))
(set-fontset-font t 'siddham (font-spec :family "Noto Sans Siddham"))
(set-fontset-font t 'sinhala (font-spec :family "Noto Sans Sinhala"))
(set-fontset-font t 'sundanese (font-spec :family "Noto Sans Sundanese"))
(set-fontset-font t 'syloti-nagri (font-spec :family "Noto Sans Syloti Nagri"))
(set-fontset-font t 'tamil (font-spec :family "Noto Sans Tamil"))
(set-fontset-font t 'telugu (font-spec :family "Noto Sans Telugu"))
(set-fontset-font t 'tagalog (font-spec :family "Noto Sans Tagalog"))
(set-fontset-font t 'tagbanwa (font-spec :family "Noto Sans Tagbanwa"))
(set-fontset-font t 'tai-viet (font-spec :family "Noto Sans Tai Viet"))
(set-fontset-font t 'thai (font-spec :family "Noto Sans Thai"))
(set-fontset-font t 'tibetan (font-spec :family "Noto Serif Tibetan"))
(set-fontset-font t 'tirhuta (font-spec :family "Noto Sans Tirhuta"))
(set-fontset-font t 'wancho (font-spec :family "Noto Sans Wancho"))
(set-fontset-font t 'hangul (font-spec :family "Noto Serif KR")))
;;@@ 7890 for clash
(defvar yy/proxy-checker
(make-network-process
:name "cat checker"
:service 7890
:host "127.0.0.1"
:nowait t))
(run-with-timer
5.0 nil
(lambda ()
(when (process-live-p yy/proxy-checker)
(setenv "HTTP_PROXY" "127.0.0.1:7890")
(setenv "HTTPS_PROXY" "127.0.0.1:7890"))
(delete-process yy/proxy-checker)
(setq yy/proxy-checker nil)))
;;@2 内置的 emacs 包
;;@@PACKAGE
(use-package package
:config
;; 添加 melpa 源和 yyelpa 源
;; 使 yyelpa 具有最大优先级
(setopt package-archives
'(("melpa" . "https://melpa.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("gnu" . "https://elpa.gnu.org/packages/")
("yyelpa" . "https://elpa.egh0bww1.com/packages/")))
(add-to-list 'package-unsigned-archives "yyelpa")
(setopt package-check-signature nil)
(setopt package-archive-priorities
'(("melpa" . 1) ("nongnu" . 5)
("gnu" . 10) ("yyelpa" . 100)))
(when (version< emacs-version "27.1")
(package-initialize)))
;;@@DIMINISH 放在最前面
;; 可以用来取消某些 minor-mode 字符在 modeline 的显示
(use-package diminish :ensure t :pin "gnu")
;;@@DISPLAY-LINE-NUMBERS 显示行号
(setopt line-number-display-limit
large-file-warning-threshold)
;;(setq line-number-display-limit-width 1000)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)
;;@@HL-LINE-MODE 高亮当前行
(add-hook 'prog-mode-hook 'hl-line-mode)
;;@@ELEC-PAIR 括号匹配高亮
(add-hook 'prog-mode-hook 'electric-pair-mode)
;;@@DELETE-SELECTION-MODE 在选中区域时输入内容将删除区域
(delete-selection-mode t)
;;@@UNIQUIFY 路径名显示唯一化
(setopt uniquify-buffer-name-style 'reverse
uniquify-separator " ← "
uniquify-ignore-buffers-re "^\\*")
;;@@ISEARCH “智能搜索”
(setopt isearch-allow-scroll t)
;;@@IBUFFER 高级 buffer 列表
(use-package ibuffer
:bind ("C-x C-b" . yy/ibuffer)
:config
(defun yy/ibuffer ()
(interactive)
(if (string= (buffer-name) "*Ibuffer*")
(ibuffer-update nil t)
(ibuffer)))
;; 不显示临时 BUFFER
;; 还是显示吧
;;(setopt ibuffer-never-show-predicates '("^\\*"))
;; 在其他窗口中显示 ibuffer
(setopt ibuffer-use-other-window t)
;; 不显示为空的分组
(setopt ibuffer-show-empty-filter-groups nil)
;; 不显示汇总信息
(setopt ibuffer-display-summary nil)
;; 默认的 filter-group
(setopt ibuffer-saved-filter-groups
'(("default"
("PROJECT"
(name . "\\*<p>.+\\*"))
("emacs-src-el"
(and (file-extension . "el")
(directory . "share/emacs/.*/lisp")))
("emacs-lisp"
(or (file-extension . "el")
(mode . emacs-lisp-mode)))
("common-lisp"
(or (file-extension . "lisp")
(mode . lisp-mode)))
("scheme/racket"
(or (mode . scheme-mode)
(file-extension . "scm")))
("C/C++"
(or (mode . c-mode)
(mode . c++-mode)
(filename . ".+\\.\\(c\\|cc\\|cpp\\|h\\|hpp\\)")))
("Python"
(or (mode . python-mode)
(mode . python-ts-mode)
(file-extension . "py")))
("js/css/html"
(or (mode . js-mode)
(mode . js-ts-mode)
(mode . json-ts-mode)
(filename . ".+\\.\\(cjs\\|mjs\\|js\\|json\\|ts\\)")
(mode . html-mode)
(mode . css-mode)
(filename . ".+\\.wgsl")
(filename . ".+\\.html?")
(filename . ".+\\.css")))
("Rust"
(or (mode . rust-ts-mode)
(file-extension . "rs")))
("rescript"
(or (mode . rescript-mode)
(filename . ".+\\.resi?")))
("ORG"
(or (mode . org-mode)
(file-extension . "org")))
("DIRED"
(mode . dired-mode))
("IMAGES"
(or (mode . image-mode)
(filename . ".+\\.\\(jpe?g\\|png\\|gif\\|webp\\|ppm\\|pgm\\|pbm\\)")))
("TEXT"
(or (mode . text-mode)
(filename . ".+\\.txt")))
("CONFIG"
(or (mode . conf-mode)
(filename . ".+\\.toml")
(filename . ".+\\.yaml")))
("LOG"
(or (filename . "[cC][hH][aA][nN][gG][eE][lL][oO][gG]")
(mode . change-log-mode)))
("PROCESS"
(process))
("TEMP"
(name . "\\*.*\\*")))))
(defun yy/ibuffer-use-default-group ()
(and (not ibuffer-filter-groups) ;; not use group
(assoc "default" ibuffer-saved-filter-groups)
(ibuffer-switch-to-saved-filter-groups "default")))
(add-hook 'ibuffer-hook 'yy/ibuffer-use-default-group))
;;@@DESKTOP 保存当前 emacs 状态
(use-package desktop
;; 在 Windows 上有点太慢了,还是用 recentf 算了
:disabled
:config
(desktop-save-mode 1))
;;@@RECENTF 保留最近文件打开记录
(use-package recentf
:init
;; 可在 `recentf-open-files' 中显示的最大文件数量
(setopt recentf-max-menu-items 50)
;; recentf 保存的文件数量
(setopt recentf-max-saved-items 2000)
;; 不进行保存文件的自动清理
;;(setopt recentf-auto-cleanup 'never)
:config
(recentf-mode 1)
;; 启动时显示 recentf buffer
;;(setopt initial-buffer-choice 'recentf-open-files)
;; 每三小时保存一次最近文件列表
(defvar yy/recentf-save-timer nil)
(unless yy/recentf-save-timer
(setq yy/recentf-save-timer
(run-at-time nil (* 2 60 60) 'recentf-save-list)))
(bind-key "C-c r" 'recentf-open))
;;@@SAVEPLACE 关闭 buffer 时保存光标位置
;; 和 recentf-mode 联动一下,可以保存最近打开文件在关闭时的 point 位置
(save-place-mode 1)
;;@@WINNER 保留窗口配置记录,可回退和前进
(use-package winner
:config
(winner-mode 1)
;; 给快捷键添加 repeat-mode 支持
(defvar yy/winner-repear-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "<up>") 'winner-undo)
(define-key map (kbd "<down>") 'winner-redo)
map))
(dolist (cmd '(winner-undo winner-redo))
(put cmd 'repeat-map 'yy/winner-repear-map))
:bind (("C-x <up>" . winner-undo)
("C-x <down>" . winner-redo)))
;;@@ORG org-mode 基础配置
(use-package org
:bind (("C-c l" . org-store-link)
("C-c a" . org-agenda)
("C-c c" . org-capture))
:config
(setopt org-tags-column 0)
(setopt org-startup-truncated nil)
(setopt org-image-actual-width 400)
(setopt org-element-cache-persistent nil)
(setopt org-export-dispatch-use-expert-ui t)
(add-hook 'org-mode-hook 'visual-line-mode)
)
;;@@CC-MODE C 系语言基础配置
(use-package cc-mode
:config
(setq-default c-default-style
'((java-mode . "java")
(awk-mode . "awk")
(other . "linux")))
(setq-default c-recognize-knr-p nil)
;;(setq c-basic-offset 4)
(setopt c-electric-pound-behavior '(alignleft)))
;;@@HIPPIE 方便的路径补全
(use-package hippie-exp
:bind ("C-c TAB" . hippie-expand))
;;@@NATIVE-COMP
;; https://misohena.jp/blog/2023-07-31-setup-emacs-29-1-for-windows.html
;;te 在 bin/ 下添加必要库
;; 注意 bin/ 中 libgccjit-0.dll 存在以下依赖项:
;; 可使用 https://github.com/lucasg/Dependencies 获取 DLL 的依赖项
;; system32/{advapi32.dll, kernel32.dll, MSVCRT.dll}
;; libgcc_s_seh-1.dll, libgmp-10.dll
;; libisl-23.dll, libmpc-3.dll, libmpfr-6.dll ; 注意这三个
;; libwinpthread-1.dll, zlib1.dll, libzstd.dll
;; 随后,在 /lib/gcc/ 下添加以下内容:(一共 18 个)
;; mingw64/bin/ 中的 as.exe 和 ld.exe,它们依赖同目录下的 libzstd.dll 和 zlib1.dll
;; mingw64/lib 下的以下文件:
;; crtbegin.o, crtend.o, dllcrt2.o
;; lib{ advapi32.a, gcc_s.a, kernel32.a, mingw32.a
;; mingwex.a, moldname.a, msvcrt.a, pthread.a
;; shell32.a, user32.a}
;; mingw64/lib/gcc/x86_64-w64-mingw32/13.1.0 中的 libgcc.a
;; 忽略 native-comp 过程中的 warning
;; (setopt native-comp-async-report-warnings-errors nil)
;; 添加编译的工具和库位置
;; 似乎是不必要的
;; (when (and (fboundp #'native-comp-available-p)
;; (native-comp-available-p)
;; (eq system-type 'windows-nt))
;; (setopt native-comp-driver-options
;; (list "-B" (expand-file-name (file-name-concat
;; invocation-directory
;; "../lib/gcc")))))
;;@@TREESIT 基于语法树的 PL 前端工具
;;te 使用 `yy/treesit-install-all-languages' 安装 tree-sitter
;; https://www.nathanfurnal.xyz/posts/building-tree-sitter-langs-emacs/
(use-package treesit
:when (treesit-available-p)
:init
(setq treesit-language-source-alist
'((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
(c . ("https://github.com/tree-sitter/tree-sitter-c"))
(cmake . ("https://github.com/uyha/tree-sitter-cmake"))
(cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
(css . ("https://github.com/tree-sitter/tree-sitter-css"))
(go . ("https://github.com/tree-sitter/tree-sitter-go"))
(html . ("https://github.com/tree-sitter/tree-sitter-html"))
(javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
(typescript . ("https://github.com/tree-sitter/tree-sitter-typescript"
nil "typescript/src"))
(tsx . ("https://github.com/tree-sitter/tree-sitter-typescript"
nil "tsx/src"))
(json . ("https://github.com/tree-sitter/tree-sitter-json"))
(lua . ("https://github.com/Azganoth/tree-sitter-lua"))
(make . ("https://github.com/alemuller/tree-sitter-make"))
(python . ("https://github.com/tree-sitter/tree-sitter-python"))
(php . ("https://github.com/tree-sitter/tree-sitter-php" nil "php/src"))
(ruby . ("https://github.com/tree-sitter/tree-sitter-ruby"))
(rust . ("https://github.com/tree-sitter/tree-sitter-rust"))
(sql . ("https://github.com/m-novikov/tree-sitter-sql"))
(toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
(zig . ("https://github.com/GrayJack/tree-sitter-zig"))))
:config
(defun yy/treesit-install-all-languages ()
"Install all languages specified by `treesit-language-source-alist'."
(interactive)
(let ((languages (mapcar 'car treesit-language-source-alist)))
(dolist (lang languages)
(if (treesit-language-available-p lang)
(progn (message "`%s' parser already exists" lang)
(sit-for 0.5))
(treesit-install-language-grammar lang)
(message "`%s' parser was installed." lang)
(sit-for 0.75))))))
;;@@PIXEL-SCROLL
;; https://emacs-china.org/t/topic/25114/4
;;te 是否使用像素滚动翻页
(yy/defcustom yy/use-pixel-scrool nil
"是否使用像素滚动"
:type 'boolean)
(when yy/use-pixel-scrool
(use-package pixel-scroll
:config
(pixel-scroll-precision-mode 1)
(setq pixel-scroll-precision-interpolate-page t)
(defun yy/pixel-scroll-interpolate-down (&optional lines)
(interactive)
(if lines
(pixel-scroll-precision-interpolate (* -1 lines (pixel-line-height)))
(pixel-scroll-interpolate-down)))
(defun yy/pixel-scroll-interpolate-up (&optional lines)
(interactive)
(if lines
(pixel-scroll-precision-interpolate (* lines (pixel-line-height))))
(pixel-scroll-interpolate-up))
(defalias 'scroll-up-command 'yy/pixel-scroll-interpolate-down)
(defalias 'scroll-down-command 'yy/pixel-scroll-interpolate-up)))
;;@@REPEAT-MODE 反复触发一组命令
;; 重复按键可以使用某个键反复触发,比如 C-x o o o ...
;; 可通过 `describe-repeat-maps' 来了解哪些命令可用
(repeat-mode 1)
;;@@ELDOC echo area 位置的短文档
;; 让 eldoc 显示的快一点
(setopt eldoc-idle-delay 0.3)
;;@@PROJECT 配置项目管理功能
(use-package project
:bind ("C-x p C-b" . yy/project-list-buffers)
:config
;; 使用 ibuffer 显示属于当前项目的文件
;; 可通过 `g' 键更新 ibuffer
(defun yy/project-list-buffers (&optional arg)
(interactive "P")
(let* ((pr (project-current t))
(root (expand-file-name (project-root pr)))
(name (format "*<p>%s*"
(file-name-nondirectory
(directory-file-name root)))))
(ibuffer t name)
(ibuffer-filter-disable)
(ibuffer-filter-by-filename root))))
;;@@WEBJUMP 通过 emacs 触发浏览器搜索
;; 可通过 webjump 直接 google
(use-package webjump
:config
(add-to-list 'webjump-sites
'("Google" .
[simple-query "www.google.com"
"www.google.com/search?q=" ""])))
(defun yy/google ()
"谷歌一下"
(interactive)
(let ((str (if (region-active-p)
(prog1
(buffer-substring (region-beginning) (region-end))
(deactivate-mark))
(read-string "Google: "))))
(browse-url (concat "https://google.com/search?q=" str))))
;;@@AUTO-REVERT-MODE 当文件在外部被修改时,自动更新对应的 buffer
(global-auto-revert-mode)
;;@@FILESETS 可用于保存一系列常用的文件,方便打开
;;te 添加一组自己常用的文件来让 emacs 启动时自动打开
;; 使用 `filesets-add-buffer' 添加 buffer 到 group 中
;; 使用 `filesets-edit' 来编辑 filesets group
(use-package filesets
:config
;;(filesets-init)
;; 默认在启动时打开的一组 buffer,它们一般平时很有用
(when (filesets-get-fileset-from-name "FREQ")
(with-eval-after-load 'init
(filesets-open nil "FREQ"))))
;;@@WINDMOVE 提供根据位置在 window 间移动 point 的方法
(use-package windmove
:init
(setopt windmove-wrap-around t)
:config
(windmove-mode t)
;; 使用 Hyper + wasd 在窗口间移动光标
;;(windmove-default-keybindings 'hyper)
:bind (("H-a" . windmove-left)
("H-d" . windmove-right)
("H-w" . windmove-up)
("H-s" . windmove-down)))
;;@@TAB-BAR 多个窗口配置空间
(use-package tab-bar
:config
;; 不使用默认配色,根本区分不了,草
;; 2024-05-02,modus 自带了 tab-bar 的 face,不用自己设置了
;; (custom-set-faces
;; '(tab-bar-tab ((t (:box (:line-width (1 . 1) :style released-button) :background "pale green" :inherit tab-bar))))
;; '(tab-bar-tab-inactive ((t (:background "gray75" :inherit tab-bar-tab)))))
;; 给 tab-bar 添加更大的加号和删除号
;; https://www.reddit.com/r/emacs/comments/16yeb5x/comment/k39fyta/?utm_source=share&utm_medium=web2x&context=3
(define-icon tab-bar-new nil
`((image "tabs/new.xpm"
:width 20 :height 20
:margin ,tab-bar-button-margin
:ascent center)
;; (emoji "➕")
;; (symbol "+")
(text " + "))
"Icon for creating a new tab."
:version "29.1"
:help-echo "New tab"
:group tab-bar)
(define-icon tab-bar-close nil
`((image "tabs/close.xpm"
:width 20
:height 20
:margin ,tab-bar-button-margin
:ascent center)
;; (emoji " ❌")
;; (symbol "✕") ;; "ⓧ"
(text " x"))
"Icon for closing the clicked tab."
:version "29.1"
:help-echo "Click to close tab"
:group tab-bar)
(define-icon tab-bar-menu-bar nil
'(;; (emoji "🍔")
(symbol "☰")
(text "Menu" :face tab-bar-tab-inactive))
"Icon for the menu bar."
:version "29.1"
:help-echo "Menu bar"
:group tab-bar)
(tab-bar-mode 1))
;;@@SAVEHIST-MODE 保存 minibuffer 的 s 表达式求值历史
(savehist-mode t)
;;@@PRETTIFY-SYMBOLS-MODE 优化某些符号的显示
;; 全局打开
(global-prettify-symbols-mode)
;;@@EGLOT
;; 加载 eglot
(require 'eglot)
(setopt eglot-report-progress nil)
;; 关掉 flymake 的一些提示信息,太吵了
(add-to-list 'eglot-stay-out-of 'flymake)
;; 取消 eglot 的 log 消息,我不是 LSP server 的开发者
(setopt eglot-events-buffer-size 0)
;;@@DABBREV 根据 buffer 内容进行补全
(require 'dabbrev)
(defun yy/dabbrev-capf ()
"对 `dabbrev-capf' 的简单包装"
(dabbrev--reset-global-variables)
(setq dabbrev--check-other-buffers nil)
(setq dabbrev--check-all-buffers nil)
(let* ((abbrev (dabbrev--abbrev-at-point))
(beg (progn (search-backward abbrev) (point)))
(end (progn (search-forward abbrev) (point)))
(ignore-case-p (dabbrev--ignore-case-p abbrev))
(list 'uninitialized)
(table
(lambda (s p a)
(if (eq a 'metadata)
`(metadata (cycle-sort-function . ,#'identity)
(category . dabbrev))
(when (eq list 'uninitialized)
(save-excursion
;;--------------------------------
;; New abbreviation to expand.
;;--------------------------------
(setq dabbrev--last-abbreviation abbrev)
;; Find all expansion
(with-temp-message (current-message)
(let* ((inhibit-message t)
(completion-list
(dabbrev--find-all-expansions abbrev ignore-case-p))
(completion-ignore-case ignore-case-p))
(setq list
(cond
((not (and ignore-case-p dabbrev-case-replace))
completion-list)
((string= abbrev (upcase abbrev))
(mapcar #'upcase completion-list))
((string= (substring abbrev 0 1)
(upcase (substring abbrev 0 1)))
(mapcar #'capitalize completion-list))
(t
(mapcar #'downcase completion-list))))))))
(complete-with-action a list s p)))))
(list beg end table)))
;; 将 dabbrev-capf 作为默认的 capf 之一,提供最基础的补全功能
;; (add-to-list 'completion-at-point-functions 'yy/dabbrev-capf)
;;@@ISPELL, 30 里面的某些设置
(setopt text-mode-ispell-word-completion nil)
;;@@MODUS-THEME 试试 Emacs 内置的 modus 主题
(dolist (c (copy-sequence custom-enabled-themes))
(disable-theme c))
(cond
((>= emacs-major-version 30)
(load-theme 'modus-operandi-tinted nil t)
(load-theme 'modus-vivendi-deuteranopia nil t)
(if (or (>= (nth 2 (decode-time (current-time))) 18) (<= (nth 2 (decode-time (current-time))) 6))
(enable-theme 'modus-vivendi-deuteranopia)
(enable-theme 'modus-operandi-tinted)))
((>= emacs-major-version 29)
(load-theme 'modus-operandi nil t)
(enable-theme 'modus-operandi))
(t nil))
(when (>= emacs-major-version 30)
(defun yy/toggle-modus-theme ()
"切换 modus 主题"
(interactive)
(let ((theme (car custom-enabled-themes)))
(cond
((eq theme 'modus-operandi-tinted)
(disable-theme theme)
(enable-theme 'modus-vivendi-deuteranopia))
((eq theme 'modus-vivendi-deuteranopia)
(disable-theme theme)
(enable-theme 'modus-operandi-tinted))
(t (message "Seems current theme is not modus's series"))))))
;;@@PAREN 配置 Emacs 内置的括号显示功能
;; 让光标在紧贴括号内部时也显示高亮
(setopt show-paren-when-point-inside-paren t)
;; 让光标在一行时,高亮显示该行的最外层括号
(setopt show-paren-when-point-in-periphery t)
;; 使用 overlay 显示当前屏幕内不可见的括号上下文
;; 可以使用 t(echo-area), 'overlay, 'child-frame
(setopt show-paren-context-when-offscreen 'overlay)
;;@3 外部安装的 elisp 包
;;@@ 指定需要安装的包
(setopt package-selected-packages
'(;; 安装的包列表
markdown-mode ; 提供 emacs 中的 markdown 支持
;;parrot ; modeline 上的沙雕鹦鹉,点击播放 gif
cnfonts ; 中英文字体对其,通过 `cnfonts-edit-profile' 进行设定
find-file-in-project ; ffip,查找项目中的文件
winum ; 快速的窗口间跳转
pyim ; 拼音输入法
pyim-basedict ; pyim 依赖项
popup ; pyim 依赖项
asmd ; yyelpa 上的第一个包
;;moe-theme ; 萌主题
vertico ; M-x 补全
;;company ; company 补全
;;company-posframe ; company 的 childframe 补全界面
eldoc-box ; 提供悬浮的 eldoc 补全
vundo ; 可视化 undo,undo-tree 的替代品
magit ; 强大的 git UI
ibuffer-vc ; 为 ibuffer 添加基于项目的分组
orderless ; 乱序补全后端
consult ; 异步查询框架
wgrep ; 基于 grep 的批量替换工具
haskell-mode ; haskell 语言支持
igist ; emacs 的一个 gist 客户端
devdocs ; 提供来自 devdocs 的文档支持
buffer-env ; 提供基于 direnv 的 buffer-local 环境
expand-region ; 选中扩展
which-key ; 按键辅助,提示按键对应的命令
diminish ; 让某些 mode 标识不在 modeline 显示
;;yasnippet ; 强大的代码模板工具
breadcrumb ; 面包屑导航
blacken ; python blacken 的 emacs 集成
shrface ; 更美观的 HTML 渲染
docker ; emacs 的 docker 集成
;;benchmark-init ; 收集 emacs 的启动信息
;;tabspaces ; 提供基于 tab-bar 的 session 管理
;;popwin ; 快速关闭各种临时 buffer
;;hyperbole ; 远古插件,也许很强大
embark ; DWIM 工具
embark-consult
popper ; 类似 popwin 但更好用
expreg ; 类似 expand-region,但更好
corfu ; corfu 补全前端,company 的替代品
tempel ; 代码模板引擎
cape ; 为 corfu 提供一些后端
envrc ; 类似 buffer-env
citre ; ctags 的 emacs 前端
powershell ; 在 Emacs 中打开 powershell, windows 下可用
zig-mode
typescript-mode
emacsql ; 统一的 SQL 前端
f ; 稍微好用一点的文件 API
))
(package-install-selected-packages)
;;@@ 安装来自 git 仓库的包
(setopt package-vc-selected-packages
'(
(yyorg-bookmark :url "https://github.com/include-yy/yyorg-bookmark")
(chodf :url "https://github.com/include-yy/chodf")
(rescript-mode :url "https://github.com/include-yy/rescript-mode")
(ox-w3ctr :url "https://github.com/include-yy/ox-w3ctr")
(tetosave :url "https://github.com/yyelpa/tetosave") ; 自动保存
;;(consult-everything :url "https://github.com/jthaman/consult-everything")
;;(wgsl-ts-mode :url "https://github.com/acowley/wgsl-ts-mode")
(yynt :url "https://github.com/include-yy/yynt")
))
(package-vc-install-selected-packages)
;;@@ emacs 29 的 package-vc-upgrade 似乎有点问题...
;; https://emacs-china.org/t/package-vc-upgrade-error-file-is-not-under-version-control/27073
;; https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-04/msg01407.html
(when (eq emacs-major-version 29)
(defun yy/package-vc-upgrade-advice (upfun pkg-desc)
(with-temp-buffer
(funcall upfun pkg-desc)))
(advice-add 'package-vc-upgrade :around 'yy/package-vc-upgrade-advice))
;;@@MARKDOWN-MODE
(use-package markdown-mode
:mode "\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\|mdx\\)\\'")
;;@@PARROT 沙雕鹦鹉
;; (parrot-mode 1)
;;@@CNFONTS 中英文字体对齐
(cnfonts-mode 1)
;;@@FIND-FILE-IN-PROJECT 方便的项目内文件搜索
;;te 设置 ffip 在 Windows 下的相关选项
;; 在 Windows 上需要设定 `ffip-find-executable' 为 find
;; 也可考虑使用 rust 编写的 fd,设置 `ffip-use-rust-fd' 为 `t'
;; 并设定 `ffip-find-executable' 为 fd 路径
;; 注意:使用 fd 必须设定 `ffip-use-rust-fd',fd 与 find 命令行参数不一致
(use-package find-file-in-project
:bind ("C-c f" . find-file-in-project)
:config
(yy/add-to-group 'ffip-find-executable)
(yy/add-to-group 'ffip-use-rust-fd))
;;@@WINUM 窗口间跳转
(use-package winum
:config
(set-face-attribute 'winum-face nil
:foreground "DeepPink"
:underline "DeepPink"
:weight 'bold)
(winum-set-keymap-prefix (kbd "C-c"))
(winum-mode 1))
;;@@PYIM elisp 实现的中文输入法
(use-package pyim
:init (setopt default-input-method "pyim")
:defer t
:config
(use-package pyim-basedict)
(use-package popup)
(pyim-basedict-enable)
(pyim-default-scheme 'microsoft-shuangpin))
;;@@MOE-THEME 萌主题
(use-package moe-theme
:disabled
:config
(moe-light))
;;@@VERTICO 更好的 icomplete 和 ido
;;(ido-mode 1)
;;(icomplete-mode 1)
;;(icomplete-vertical-mode 1)
(vertico-mode 1)
;;@@COMPANY company 补全前端
(use-package company
:diminish company-mode
:disabled
:config
(global-company-mode 1)
(setq company-global-modes '(not shell-mode eshell-mode org-mode))
;; front-end configure
(setq company-mininum-prefix-length 3) ; 3 char to pop up menu
(setq company-idle-delay 0) ; set pop-up delay to 0s
(setq company-echo-delay 0) ; remove annoying blink
(setq company-require-match nil) ; allow string don't match candidate words
(setq company-show-numbers t) ; show candidates order number 1 ~ 9 and 0
(setq company-selection-wrap-around t) ; loop over candidates
(setq company-auto-commit nil) ; don't use auto-complete
;; Use the tab-and-go frontend
;; Allow TAB to select and complete at the same time
(company-tng-configure-default)
(setq company-frontends
'(company-tng-frontend
company-pseudo-tooltip-frontend
company-echo-metadata-frontend))
;;Customize company backends
(setq company-backends (delete 'company-bbdb company-backends))
(setq company-backends (delete 'company-oddmuse company-backends))
;; Don't downcase the returned candidate
(setq company-dabbrev-downcase nil)
(setq company-dabbrev-ignore-case t))
;;@@COMPANY-POSFRAME 为 company 提供浮动子窗口支持
(use-package company-posframe
:diminish company-posframe-mode
:after company
:config
(company-posframe-mode 1)
;;dont display annoying quickhelper
(setq company-posframe-quickhelp-delay nil))
;;@@CORFU 试试更加轻量和现代的 corfu
(use-package corfu
:init
;; 允许 cycle
(setopt corfu-cycle t)
;; 允许自动触发 corfu 补全
(setopt corfu-auto t)
;; 在补全时若候选项小于 3 则不弹出选项
(setopt completion-cycle-threshold 3)
;; 在输入字符 0.1 秒后触发 corfu
(setopt corfu-auto-delay 0.1)
;; 无候选项不显示 no match
(setopt corfu-quit-no-match t)
:config
(global-corfu-mode)
(defun corfu-enable-always-in-minibuffer ()
"Enable Corfu in the minibuffer if Vertico/Mct are not active."
(unless (or (bound-and-true-p mct--active)
(bound-and-true-p vertico--input)
(eq (current-local-map) read-passwd-map))
;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
(setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
corfu-popupinfo-delay nil)
(corfu-mode 1)))
(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)
(add-hook 'eshell-mode-hook
(lambda ()
(setq-local corfu-auto nil)
(corfu-mode))))
;;@@ELDOC-BOX 悬浮式 ELDOC
(add-hook 'eglot-managed-mode-hook
#'eldoc-box-hover-mode)
;;@@VUNDO 可视化 undo,比 undo-tree 更好
(use-package vundo
:bind ("C-?" . vundo))
;;@@ORDERLESS 好用的模糊搜索插件
(use-package orderless
:config
(setopt completion-styles '(orderless basic))
(setopt completion-category-overrides
'((file (styles basic partial-completion))))
;; 让 company 使用 basic
;; We follow a suggestion by company maintainer u/hvis:
;; https://www.reddit.com/r/emacs/comments/nichkl/comment/gz1jr3s/
(defun company-completion-styles (capf-fn &rest args)
(let ((completion-styles '(basic partial-completion)))
(apply capf-fn args)))
(advice-add 'company-capf :around #'company-completion-styles))
;;@@CONSULT 异步搜索框架
;;te 配置 consult 的 grep 和 ripgrep 位置
;; 在 Windows 下,注意修改 consult-grep-args 中的 grep 为 msys2 路径
;; 非常奇怪,在 customzie 中这个选项无法显示
;; 只能打个补丁 `yy/consult-grep-args'
;; `consult-ripgrep-args' 可以正常显示,所以就不在这里改了
;; 常用命令:
;; - `consult-grep', `consult-ripgrep',`consult-git-grep' grep 当前项目的内容
;; - `consult-line', 可视化 buffer 搜索
;; - `consult-buffer', 强化版切换 `switch-to-buffer'
;; - `consult-goto-line', 可视化切换行
;; 似乎又能够正常显示了,暂时用不到这个 option 了
(yy/defcustom yy/consult-grep-args nil
"用于 consult 的 grep 命令行参数"
:type 'boolean)
(use-package consult
:bind
(("C-c s" . consult-line)
("M-g g" . consult-goto-line))
:config
(when yy/consult-grep-args
(setq consult-grep-args yy/consult-grep-args)))
;;@@WGREP 可对 grep 结果进行编辑
;; C-c C-p 进入编辑模式, C-x C-s 应用编辑结果, C-x C-q 结束编辑
;; C-c C-d 标记删除某行
;; C-c C-k 撤销所有变化并退出
(use-package wgrep)
;;@@TETOSAVE 自动保存 buffer,无需频繁使用 C-x C-s 了
(use-package tetosave
:config
(setopt tetosave-idle 0.5)
(tetosave-enable))
;;@@IGIST 好用的 gist 管理工具
;;te 需要设置 `igist-auth-marker' 为 oauth token
;; token 的获取参考 https://github.com/KarimAziev/igist/tree/main#auth
(use-package igist
:after tetosave
:bind ("M-o" . igist-dispatch)
:config
(setopt igist-current-user-name "include-yy")
;; disable tetosave for igist-edit-mode
(add-to-list 'tetosave-disable-predicates
(defun igist/teto-pred () igist-edit-mode))
;; disable tetosave for gpg file
(add-to-list 'tetosave-disable-predicates
(defun igist/teto-pred-authinfo ()
(eq major-mode 'authinfo-mode))))
;;@@EXPAND-REGION 方便的 region 扩展工具
(use-package expand-region
;; 现在可以试试 Yuan fu 的 expreg
:when (not (treesit-available-p))
:bind ("C-=" . er/expand-region))
;;@@EXPREG 可以使用 tree sitter 的 expand-region
(use-package expreg
:when (treesit-available-p)
:bind ("C-=" . expreg-expand))
;;@@WHICH-KEY 显示某个按键绑定的 keymap 中的命令
(use-package which-key
:diminish which-key-mode
:init
;; 通过 C-h 或 ? 才显示 which-key buffer
;; 如果某些命令使用了 C-h 或 ? 可能会冲突
(setopt which-key-show-early-on-C-h t)
;; 仅通过 C-h 触发
(setopt which-key-idle-delay 10000.0)
;; 在随后的按键中迅速响应
(setopt which-key-idle-secondary-delay 0.05)
:config
;; 启动全局 which-key-mode
(which-key-mode))
;;@@BREADCRUMB 面包屑导航
(breadcrumb-mode)
;;@@YASNIPPET 代码模板系统,提供好用的代码模板
(use-package yasnippet
;; 试试 tempo 去
:disabled)
;;@@SHRFACE,更好看的 html 渲染
(use-package shrface
:config
:disabled
(shrface-basic)
(shrface-trial)
(shrface-default-keybindings)
(setopt shrface-href-versatile t))
;;@@DOCKER 方便的 docker emacs 集成
(use-package docker
:if (eq system-type 'gnu/linux)
:bind ("C-c d" . docker))
;;@@TABSPACE 使用 tab-bar-mode 和 project.el 管理项目
(use-package tabspaces
:disabled
:init
(setopt tabspaces-default-tab "Default")
(setopt tabspaces-initialize-project-with-todo nil)
;;(setopt tabspaces-use-filtered-buffers-as-default t)
;; sessions
(setopt tabspaces-session t)
(setopt tabspaces-session-auto-restore t)
:config
(tabspaces-mode))
;;@@POPWIN 方便地从 *Help*, *Completions* 等临时 buffer 中通过 C-g 退出
(use-package popwin
;; 感觉不如 popper
:disabled
:config
(popwin-mode))
;;@@EMBARK 归一化的动作执行者
(use-package embark
:bind
(("C-." . embark-act)
("C-;" . embark-dwim)))
;;@@EMBARK-CONSULT 联动 embark 与 consult
(use-package embark-consult
:hook
(embark-collect-mode . consult-preview-at-point-mode))
;;@@HYPERBOLE 暂时不知道怎么用
;; 它会占用 C-h h ,原本用于打开 Hello buffer
(use-package hyperbole
:disabled)
;;@@POPPER 管理弹出的各种临时 buffer
;; 这样就不会打破 Window 布局
(use-package popper
:bind (("C-`" . popper-toggle)
("M-`" . popper-cycle)
("C-M-`" . popper-toggle-type))
:init
(setopt popper-reference-buffers
'("\\*Messages\\*" "Output\\*$"
"\\*Async Shell Command\\*"
"\\*Completions\\*"
"\\*eshell.*\\*" eshell-mode
"\\*Occur\\*"
help-mode
compilation-mode
shell-mode "^\\*shell.*\\*"
"\\*xref\\*"))
:config
(popper-mode 1)
(popper-echo-mode 1))
;;@@ENVRC Linux 上管理开发环境
;; 可以执行 `envrc-allow' 和 `envrc-deny' 来开启和关闭某目录下的 .envrc
(use-package envrc
:when (eq system-type 'gnu/linux)
:config
(envrc-global-mode))
;;@@TEMPEL 好用的代码模板工具
(use-package templ
:bind (("M-+" . tempel-complete)
("M-*" . tempel-insert))
:init
(defun tempel-setup-capf ()
;; Add the Tempel Capf to `completion-at-point-functions'.
;; `tempel-expand' only triggers on exact matches. Alternatively use
;; `tempel-complete' if you want to see all matches, but then you
;; should also configure `tempel-trigger-prefix', such that Tempel
;; does not trigger too often when you don't expect it. NOTE: We add
;; `tempel-expand' *before* the main programming mode Capf, such
;; that it will be tried first.
(setq-local completion-at-point-functions
(cons #'tempel-expand
completion-at-point-functions)))
(add-hook 'prog-mode-hook 'tempel-setup-capf))
;;@@CITRE 先进的 ctags 前端
(use-package citre
:init
(use-package citre-config))
;;@@OX-W3CTR 我写的 org 导出后端
(require 'ox-w3ctr)
;;@4 一些零散的小函数和设定
;;@@ 打开 init 文件
(defun yy/open-init ()
"Open my init file"
(interactive)
(find-file "~/.emacs.d/init.el"))
;;@@ 执行 yynt.el 的初始化
(defun yy/open-yynt ()
"eval yynt2.el whole buffer"
(interactive)
(if-let* ((buf (get-buffer "yy.el")))
(with-current-buffer buf
(eval-buffer))
(message "yy.el not open")))
;;@@ 增强 C-x <num> 系列的 Window 管理功能
(defun yy/split-and-move-to-other-window (fun)
(lambda (&optional arg)
(interactive "P")
(funcall fun)
(let ((target-window (next-window)))
(set-window-buffer target-window (other-buffer))
(unless arg
(select-window target-window)))))
(defalias 'yy/split-window-below (yy/split-and-move-to-other-window
'split-window-below))
(defalias 'yy/split-window-right (yy/split-and-move-to-other-window
'split-window-right))
(defun yy/delete-other-windows ()
(interactive)
(if (and winner-mode
(equal (selected-window) (next-window)))
(winner-undo)
(delete-other-windows)))
(defun yy/split-change-place (fun)
"Kill any other windows and re-split current window in one way.
the current window is on the left/top half of the frame.
way-func can be | or _"
(lambda ()
(interactive)
(let ((other-buf (and (next-window) (window-buffer (next-window)))))
(delete-other-windows)
(funcall fun)
(when other-buf
(set-window-buffer (next-window) other-buf)))))
(defalias 'yy/split-change-right (yy/split-change-place 'split-window-right))
(defalias 'yy/split-change-below (yy/split-change-place 'split-window-below))
(bind-key "C-x 1" 'yy/delete-other-windows)
(bind-key "C-x 2" 'yy/split-window-below)
(bind-key "C-x 3" 'yy/split-window-right)
(bind-key "C-x |" 'yy/split-change-right)
(bind-key "C-x _" 'yy/split-change-below)
;;@@ 显示修改过的按键绑定
(defun yy/show-keys ()
"显示所有通过 `use-package' 或 `bind-key' 修改的按键绑定"
(interactive)
(describe-personal-keybindings))
;;@@ 文字计数,可以处理中文和英文
;; Author: Andy Stewart <lazycat.manatee@gmail.com>
;; http://www.emacswiki.org/emacs/download/basic-toolkit.el
(defun count-ce-words (beg end)
"Count Chinese and English words in marked region."
(interactive "r")
(let ((cn-word 0)
(en-word 0)
(total-word 0)
(total-byte 0))
(setq cn-word (count-matches "\\cc" beg end)
en-word (count-matches "\\w+\\W" beg end))
(setq total-word (+ cn-word en-word)
total-byte (+ cn-word (abs (- beg end))))
(message (format "Total: %d (CN: %d, EN: %d) words, %d bytes."
total-word cn-word en-word total-byte))))
;;@@ 删除 *{name}* 的 buffer
(defun kill-unused-buffers ()
(interactive)
(ignore-errors
(save-excursion
(dolist (buf (buffer-list))
(set-buffer buf)
(if (and (string-prefix-p "*" (buffer-name))
(string-suffix-p "*" (buffer-name)))
(kill-buffer buf))))))
;;@@ 对整个 buffer 进行缩进
(defun indent-buffer ()
"Automatic format current buffer."
(interactive)
(save-excursion
(indent-region (point-min) (point-max) nil)
(delete-trailing-whitespace)
(untabify (point-min) (point-max))))
;;@@IGIST 使用 igist 管理 init.el 配置文件
(require 'igist)
(require 'transient)
(defun yy/upload-init-file ()
"将 init.el 更新到对应 gist"
(interactive)
(when-let* ((initbuf (get-file-buffer (file-name-concat user-emacs-directory "init.el")))
(gistbuf (current-buffer)))
(if (not (with-current-buffer gistbuf
(bound-and-true-p yy-init-file)))
(user-error "not in igist buffer init.el")
(let ((inhibit-read-only t))
(with-current-buffer initbuf
(copy-to-buffer gistbuf (point-min) (point-max)))
(with-current-buffer gistbuf
(igist-save-current-gist-and-exit))))))
(defun yy/update-init-file ()
"将 igist init buffer 中的内容更新到本地的 init.el"
(interactive)
(when-let* ((initbuf (get-file-buffer (file-name-concat user-emacs-directory "init.el")))
(gistbuf (current-buffer)))
(if (not (with-current-buffer gistbuf
(bound-and-true-p yy-init-file)))
(user-error "not in igist buffer init.el")
(let ((inhibit-read-only t))
(with-current-buffer gistbuf
(copy-to-buffer initbuf (point-min) (point-max)))
(kill-buffer gistbuf)))))
(defun yy/diff-init-and-igist (&optional reverse)
"对 init.el 和 igist init buffer 调用 `diff-buffers'"
(interactive)
(when-let* ((initbuf (get-file-buffer (file-name-concat user-emacs-directory "init.el")))
(gistbuf (current-buffer)))
(if (not (with-current-buffer gistbuf
(bound-and-true-p yy-init-file)))
(user-error "not in igist buffer init.el")
(if reverse
(diff-buffers initbuf gistbuf)
(diff-buffers gistbuf initbuf)))))
(defalias 'yy/diff-igist-and-init (lambda () (interactive) (yy/diff-init-and-igist t)))
(transient-define-prefix yy/init-file-transient ()
"用于 igist init buffer 的 transient 按键,简单的界面"
["diff"
("f" "观察 init 相对 gist 的改动" yy/diff-init-and-igist)
("r" "观察 gist 相对 init 的区别" yy/diff-igist-and-init)]
["update"
("u" "将 init.el 更新到 gist" yy/upload-init-file)
("d" "将 gist 内容复制到本地 init" yy/update-init-file)])
(defun yy/get-gist-init-file ()
"获取来自 gist 的 init.el 文件,可直接复制其中内容,也可修改并 C-c C-c 提交
建议在 init.el 文件中运行该命令,方便对比修改"
(interactive)
(yy/open-init)
(igist-list-load-gists
"include-yy" t
(lambda ()
(with-current-buffer (igist-get-user-buffer-name
igist-current-user-name)
(goto-char (point-min))
(search-forward "init.el")
(with-current-buffer (igist-list-edit-gist-at-point)
(setq-local yy-init-file t)
;;https://stackoverflow.com/questions/27321407/how-to-make-a-buffer-local-key-binding-in-emacs
(use-local-map (copy-keymap (current-local-map)))
(local-set-key (kbd "C-c c") 'yy/init-file-transient)
))
(kill-buffer (igist-get-user-buffer-name
igist-current-user-name)))))
;;@@YYINIT-MODE 用于 init.el 浏览各配置节点的 minor-mode,使用了 outline-minor-mode
(defvar-keymap yyinit-mode-map
:doc "部分来自 outline-mode 的键绑定"
"C-c C-n" #'outline-next-visible-heading
"C-c C-p" #'outline-previous-visible-heading
"C-c C-u" #'outline-up-heading
"C-c C-a" #'outline-show-all
"C-c C-o" #'outline-hide-other
"C-c C-j" #'consult-outline
;; 把打开 gist 上配置的功能顺便放这里算了
"C-c C-c" #'yy/get-gist-init-file)
(define-minor-mode yyinit-mode
"用于浏览配置文件各节点的 minor-mode,添加了部分 outline-mode 按键绑定"
:keymap yyinit-mode-map)
(defun yy/yyinit-setup ()
(interactive)
(when (equal (expand-file-name "~/.emacs.d/init.el")
(buffer-file-name (current-buffer)))
(setq-local outline-regexp ";;; init.el ---\\|;;; Code\\|;;@+")
(setq-local outline-heading-alist '((";;; init.el ---" . 1) (";;; Code" . 1)
(";;@" . 2) (";;@@" . 3)))
(setq-local outline-minor-mode-use-buttons 'in-margins)
(setq-local outline-minor-mode-highlight 'override)
(setq-local outline-minor-mode-cycle t)
(setq-local outline-level 'outline-level)
(outline-minor-mode)
(yyinit-mode)))
;;@@EAF, lazycat 的 Emacs 应用框架
;;te 从 eaf github 拉取仓库,在目录下创建名为 eaf-venv 的 python 虚拟环境
;; 在虚拟环境下运行 install-eaf.py
(yy/defcustom yy/use-eaf nil "是否使用 eaf")
(when (and (eq system-type 'windows-nt) yy/use-eaf)
(let ((path (file-name-concat user-emacs-directory "emacs-application-framework")))
(when (file-exists-p path)
(add-to-list 'load-path path)
(require 'eaf)
(require 'eaf-browser)
(setq eaf-python-command (file-name-concat path "eaf-venv" "Scripts" "python.exe")))))
;;@5 专门的语言特定配置区域
;;@@PYTHON 配置
;; 默认使用 python-ts-mode,需要安装 python 的 treesitter
(add-to-list 'eglot-server-programs
'((python-mode python-ts-mode) . ("pyright-langserver" "--stdio")))
;; 启用 treesit
(yy/defcustom yy-use-python-treesit nil
"是否使用 emacs 的 treesit python 支持
当前,treesit 似乎还是不够稳定")
(when (and (treesit-available-p)
yy-use-python-treesit
(treesit-ready-p 'python))
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode)))
;; linux 下的 python 是 python3, Windows 还是用 python
(when (equal system-type 'windows-nt)
(setopt python-shell-interpreter "python"))
(use-package python
:init
(defun yy/python-devdocs-current-docs ()
(setq-local devdocs-current-docs '("python~3.11")))
:bind
;; 设置 C-c r 来使用 eglot-rename,提供简单的重构功能
(:map python-base-mode-map
("C-c r" . eglot-rename) ;; 重命名
("C-+" . hs-toggle-hiding) ;; 折叠代码
("C-c d" . devdocs-lookup) ;; 查找文档
("C-c f" . blacken-buffer) ;; black 格式化
)
:hook
;; 使用 pyright 作为 lsp server
;;te 通过 pip/pipx install pyright 来安装 pyright,使用 npm 也行
(python-base-mode . eglot-ensure)
;; 使用 prettify ,将 lambda -> λ, and -> ∧, or -> ∨
(python-base-mode . prettify-symbols-mode)
;; 使用 hs-minor-mode 来折叠代码
(python-base-mode . hs-minor-mode)
;; 默认使用 python3.11 的文档
(python-base-mode . yy/python-devdocs-current-docs))
;;@@JAVASCRIPT 配置
;; 添加 treesit 支持,替换 js-mode 和 js-json-mode
;; 草,怎么感觉有时候还不如原始的 js-mode
(yy/defcustom yy/use-js-treesit nil
"是否使用 js 的 `js-ts-mode'"
:type 'boolean)
(if (and (treesit-available-p)
yy/use-js-treesit)
(progn
(when (treesit-language-available-p 'javascript)
(add-to-list 'major-mode-remap-alist '(javascript-mode . js-ts-mode)))
(when (treesit-language-available-p 'json)
(add-to-list 'major-mode-remap-alist '(js-json-mode . json-ts-mode)))
(when (treesit-language-available-p 'typescript)
(require 'typescript-ts-mode)))
(require 'typescript-mode))
;; 默认开启 LSP: typescript-language-server
;;te 可通过 npm install -g typescript 安装
(yy/defcustom yy/use-js-lsp nil
"是否使用 js 的 LSP"
:type 'boolean)
(when yy/use-js-lsp
(add-hook 'js-base-mode-hook 'eglot-ensure))
;; 设置 M-. 使用 xref
(use-package js
:config
(keymap-set js-mode-map "M-." 'xref-find-definitions)
(when (treesit-available-p)
(keymap-set js-ts-mode-map "M-." 'xref-find-definitions)))
;; 将 cjs mjs 和 ts(根据是否有 tree-sitter-ts)添加到 `auto-mode-alist'
(add-to-list 'auto-mode-alist '("\\.\\(cjs\\|mjs\\)\\'" . javascript-mode))
;;@@RESCRIPT 配置
;;te 配置 rescript lsp
;; 从 https://github.com/rescript-lang/rescript-vscode/releases/tag/1.18.0
;; 获取 lsp-server,然后解压缩将其中的 extension 文件夹放在 rescript-mode 目录下
;; 也许可以考虑添加 tree-sitter 支持
;; 项目在编译后才会有 LSP 提示
;; 从 rescript 11 开始,提供了独立的 lsp 包,可通过以下方式安装
;; npm install -g @rescript/language-server
;; npx rescript-language-server --stdio
(yy/defcustom yy/use-rescript-lsp nil
"是否使用 rescript 的 LSP"
:type '(choice (boolean)
(string :tag "Path")))
(use-package rescript-mode
;;:disabled
)
(when yy/use-rescript-lsp
(let ((cmd (if (booleanp yy/use-js-lsp)
(list "rescript-language-server" "--stdio")
(list "node" yy/use-rescript-lsp "--stdio"))))
(add-to-list
'eglot-server-programs
`(rescript-mode . ,cmd)))
(add-hook 'rescript-mode-hook 'eglot-ensure))
;;@@WGSL 配置
;; 添加 treesit 支持
;;te 需要从安装 libtree-sitter-wgsl 与 wgsl-ts-mode
;; 可自行从 https://github.com/szebniok/tree-sitter-wgsl 获取库并在 src 目录使用命令:
;; gcc -fPIC -c -I. parser.c && gcc -fPIC -c -I. scanner.c
;; gcc -fPIC -shared *.o -o libtree-sitter-wgsl.dll
;; 可使用 M-x package-vc-install https://github.com/acowley/wgsl-ts-mode 安装 ts mode
;;te 安装 wgsl lsp
;; cargo install --git https://github.com/wgsl-analyzer/wgsl-analyzer wgsl_analyzer
;; 当前似乎与 eglot 的配合存在一些问题
(use-package wgsl-ts-mode
:disabled
:config
(add-hook 'wgsl-ts-mode-hook
(defun yy/set-dabbrev-capf ()
(setq-local completion-at-point-functions
'(yy/dabbrev-capf t)))))
(yy/defcustom yy/use-wgsl-lsp nil
"是否使用 wgsl 的 LSP"
:type 'boolean)
(when (and (fboundp 'wgsl-ts-mode)
yy/use-wgsl-lsp)
(add-to-list 'eglot-server-programs
'(wgsl-ts-mode "wgsl_analyzer"))
(add-hook 'wgsl-ts-mode-hook 'eglot-ensure))
;;@@RUST 配置
(when (treesit-available-p)
(require 'rust-ts-mode))
(yy/defcustom yy/use-rust-lsp nil
"是否使用 rust lsp"
:type 'boolean)
(when yy/use-rust-lsp
;; https://gist.github.com/casouri/0ad2c6e58965f6fd2498a91fc9c66501
(add-to-list 'eglot-server-programs
`(rust-ts-mode . ("rust-analyzer" :initializationOptions
( :procMacro (:enable t)
:cargo ( :buildScripts (:enable t)
:features "all")))))
(add-hook 'rust-ts-mode-hook 'eglot-ensure))
;;@@C/C++ 配置
;; 可以使用 cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1 生成 LSP 可用的文件
(yy/defcustom yy/use-msys2-ucrt-clangd nil
"是否使用 msys2 的 ucrt 中的 clangd LSP server"
:type 'boolean)
(when (and (treesit-available-p)
(treesit-ready-p 'c)
(treesit-ready-p 'cpp))
(dolist (a '((c-mode . c-ts-mode)
(c++-mode . c++-ts-mode)
(c-or-c++-mode . c-or-c++-ts-mode)))
(add-to-list 'major-mode-remap-alist a))
(setopt c-ts-mode-indent-style 'bsd)
(setopt c-ts-mode-indent-offset 4))
(if yy/use-msys2-ucrt-clangd
(progn (add-to-list 'eglot-server-programs
`((c++-ts-mode c-ts-mode c-or-c++-mode)
;; https://stackoverflow.com/a/76077729
,(file-name-concat yy/w32-msys-root-path
"msys2_shell.cmd")
"-defterm" "-no-start" "-ucrt64" "-here"
"-c" "\"clangd\""))
(add-hook 'c-ts-mode-hook 'eglot-ensure)
(add-hook 'c++-ts-mode-hook 'eglot-ensure))
(when (executable-find "clangd")
(add-to-list 'eglot-server-programs
'((c++-ts-mode c-ts-mode c-or-c++-mode) "clangd"))
(add-hook 'c-ts-mode-hook 'eglot-ensure)
(add-hook 'c++-ts-mode-hook 'eglot-ensure)))
;; 加载一下 cmake-ts-mode 里面的 `auto-mode-alist' 设定表达式
(when (and
(treesit-available-p)
(treesit-language-available-p 'cmake))
(require 'cmake-ts-mode))
;;@@ 文件尾,file-local 变量
(run-with-idle-timer
1.0 nil
(lambda () (message "hello world, emacs-init-time is %s" (emacs-init-time))))
(provide 'init)
;; Local Variables:
;; eval: (when (fboundp 'yy/yyinit-setup) (yy/yyinit-setup))
;; End:
;;; init.el ends here
;; -*- mode: emacs-lisp; lexical-binding:t; -*-
fundamental-mode
org-mode
(quote "#+BEGIN_QUOTE" n r> n "#+END_QUOTE")
(q "{{{q(" p ")}}}")
(src "#+BEGIN_SRC " q n r n "#+END_SRC")
(isrc "src_" p "{" q "}")
(link "[[" p "]]")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment