emacs.d icon indicating copy to clipboard operation
emacs.d copied to clipboard

A simple Emacs config on Windows

#+html:

#+html:

GNU Emacs

#+html:

#+html: #+html: #+html:

#+html:

A simple Emacs config on Windows/WSL/Linux

  • 安装方法

** Windows

  1. 下载配置文件 #+begin_src shell git clone https://github.com/xhcoding/emacs.d.git ~/.emacs.d #+end_src

  2. 运行 =org-tangle.bat= 生成 =.el= 文件

  3. 启动 emacs

** Linux

*** guix pack

  1. 下载 Release 里面的 =emacs-x-minimal-x86_64-linux-installer.run= 安装包

  2. 添加可执行权限 =chmod +x emacs-x-minimal-x86_64-linux-installer.run=

  3. 执行 =./emacs-x-minimal-x86_64-linux-installer.run= 将 emacs 安装到 =/opt= 目录下

  4. =emacs-x= 启动 emacs

  • 目录 :TOC:
  • [[#安装方法][安装方法]]
  • [[#early-initel][early-init.el]]
    • [[#设置配置启动模式][设置配置启动模式]]
    • [[#开启-debug-on-error][开启 debug-on-error]]
    • [[#将-gc-的阈值设置到最大避免启动的时候多次-gc-拖慢启动速度][将 gc 的阈值设置到最大,避免启动的时候多次 gc 拖慢启动速度]]
    • [[#file-handler-临时设置为-nil加快启动速度][file handler 临时设置为 nil,加快启动速度]]
    • [[#不要初始化-package][不要初始化 package]]
    • [[#禁止-frame-缩放][禁止 frame 缩放]]
    • [[#关闭菜单栏][关闭菜单栏]]
    • [[#关闭工具栏][关闭工具栏]]
    • [[#关闭滚动条][关闭滚动条]]
    • [[#启动后全屏][启动后全屏]]
    • [[#straight-包管理][straight 包管理]]
    • [[#空闲时增量加载包][空闲时增量加载包]]
    • [[#启动速度测试][启动速度测试]]
  • [[#emacs-配置][emacs 配置]]
    • [[#载入-custom-文件][载入 custom 文件]]
    • [[#定义一些固定的常量][定义一些固定的常量]]
    • [[#常用的工具函数][常用的工具函数]]
      • [[#重命名当前文件][重命名当前文件]]
      • [[#删除当前文件][删除当前文件]]
      • [[#清理-message-buffer][清理 Message buffer]]
      • [[#开关网络代理][开关网络代理]]
      • [[#清理掉没用的-buffer][清理掉没用的 buffer]]
    • [[#基本设置][基本设置]]
      • [[#用户信息][用户信息]]
      • [[#增加-io-性能][增加 IO 性能]]
      • [[#设置编码][设置编码]]
      • [[#设置-frame-标题][设置 frame 标题]]
      • [[#关闭启动动画][关闭启动动画]]
      • [[#scratch-buffer-设置][scratch buffer 设置]]
      • [[#yes-or-no-p-改为-y-or-n-p][yes-or-no-p 改为 y-or-n-p]]
      • [[#关掉提示声音][关掉提示声音]]
      • [[#禁止光标闪烁][禁止光标闪烁]]
      • [[#启动时不要显示][启动时不要显示]]
      • [[#tab-设置][tab 设置]]
      • [[#一行不超过-120-字符][一行不超过 120 字符]]
      • [[#删除文件时放到回收站][删除文件时放到回收站]]
      • [[#高亮当前行][高亮当前行]]
    • [[#gcmh-优化垃圾回收][gcmh 优化垃圾回收]]
    • [[#auto-save-自动保存][auto-save 自动保存]]
    • [[#rime-智能的中文输入法][rime 智能的中文输入法]]
    • [[#字体和主题][字体和主题]]
    • [[#ligature-连字体][ligature 连字体]]
    • [[#awesome-tray-底部信息显示][awesome-tray 底部信息显示]]
    • [[#sort-tab-快速切换到其它-buffer][sort-tab 快速切换到其它 buffer]]
    • [[#hydra-方便的快捷键设置][hydra 方便的快捷键设置]]
    • [[#recentf-读取最近文件][recentf 读取最近文件]]
    • [[#popper-更方便的弹出窗口][popper 更方便的弹出窗口]]
    • [[#autorevert-自动同步外部文件改变][autorevert 自动同步外部文件改变]]
    • [[#minibuffer-补全及增强][minibuffer 补全及增强]]
    • [[#vundo-undo-增强][vundo undo 增强]]
    • [[#avy-快速移动光标到屏幕上任意字符][avy 快速移动光标到屏幕上任意字符]]
    • [[#ace-window-快速切换到其它-window][ace-window 快速切换到其它 window]]
    • [[#toggle-one-window-快速切换到单窗口][toggle-one-window 快速切换到单窗口]]
    • [[#yasnippet-快速插入代码片段][yasnippet 快速插入代码片段]]
    • [[#separedit-快速编辑][separedit 快速编辑]]
    • [[#projectileel-工程管理][projectile.el 工程管理]]
    • [[#compile-增强][compile 增强]]
    • [[#eshell-增强][eshell 增强]]
      • [[#eshell-基本配置][eshell 基本配置]]
      • [[#eshell-历史记录设置][eshell 历史记录设置]]
      • [[#esh-mode-设置][esh-mode 设置]]
      • [[#eshell-git-prompt-提示符美化][eshell-git-prompt 提示符美化]]
      • [[#eshell-z-智能目录跳转][eshell-z 智能目录跳转]]
    • [[#fingertip-智能括号插入][fingertip 智能括号插入]]
    • [[#color-rg-快速搜索重构][color-rg 快速搜索重构]]
    • [[#lsp-bridge-代码补全][lsp-bridge 代码补全]]
    • [[#highlight-parentheses-高亮外层括号][highlight-parentheses 高亮外层括号]]
    • [[#evil-nerd-commenter-智能注释代码][evil-nerd-commenter 智能注释代码]]
    • [[#apheleia-智能格式化代码][apheleia 智能格式化代码]]
    • [[#wuco-拼写检查][wuco 拼写检查]]
    • [[#git-配置][git 配置]]
      • [[#magit][magit]]
      • [[#显示当前行的最后提交信息][显示当前行的最后提交信息]]
      • [[#smerge-mode-解决冲突][smerge-mode 解决冲突]]
    • [[#visual-regexp-可视化的正则替换][visual-regexp 可视化的正则替换]]
    • [[#expand-region-递增选区][expand-region 递增选区]]
    • [[#openwith-外部工具打开文件][openwith 外部工具打开文件]]
    • [[#启用-treesit][启用 treesit]]
    • [[#elisp-配置][elisp 配置]]
    • [[#c-开发配置][C++ 开发配置]]
      • [[#citre][citre]]
      • [[#cmake-ts-mode-设置][cmake-ts-mode 设置]]
      • [[#c-ts-mode-设置][c-ts-mode 设置]]
      • [[#导入-vs-环境变量][导入 VS 环境变量]]
    • [[#rust-开发配置][Rust 开发配置]]
    • [[#python-开发配置][python 开发配置]]
    • [[#java-开发配置][java 开发配置]]
    • [[#web-开发][web 开发]]
      • [[#javascript][Javascript]]
      • [[#typescript][Typescript]]
      • [[#html][HTML]]
      • [[#css][css]]
    • [[#json-ts-mode][json-ts-mode]]
    • [[#qml-开发][qml 开发]]
    • [[#lua-开发][lua 开发]]
    • [[#plantuml-支持][plantuml 支持]]
    • [[#dape-调试][dape 调试]]
    • [[#dash-docs-查询-dash-文档][dash-docs 查询 dash 文档]]
    • [[#shrface-让-eww-的阅读体验更好][shrface 让 eww 的阅读体验更好]]
    • [[#eww-配置][eww 配置]]
    • [[#olivetti-居中显示内容][olivetti 居中显示内容]]
    • [[#org-配置][org 配置]]
      • [[#org-mode-美化设置][org-mode 美化设置]]
      • [[#org-基本配置][org 基本配置]]
      • [[#org-babel-设置][org babel 设置]]
      • [[#gtd-配置][gtd 配置]]
      • [[#export-设置][export 设置]]
      • [[#生成-toc][生成 TOC]]
      • [[#org-contrib-wanderlust-支持][org-contrib wanderlust 支持]]
      • [[#增量载入-org-包][增量载入 org 包]]
    • [[#知识管理及博客配置][知识管理及博客配置]]
      • [[#denote][denote]]
      • [[#org-roam][org-roam]]
      • [[#hugo-配置][hugo 配置]]
      • [[#上传博客图片到腾讯云对象存储][上传博客图片到腾讯云对象存储]]
      • [[#自动在中英文插入空格][自动在中英文插入空格]]
      • [[#org-download-下载图片][org-download 下载图片]]
    • [[#eaf-配置][eaf 配置]]
    • [[#popweb-配置][popweb 配置]]
    • [[#dictionary-overlay-方便阅读英文文章][dictionary-overlay 方便阅读英文文章]]
    • [[#邮件配置][邮件配置]]
      • [[#wanderlust-邮件前端][Wanderlust 邮件前端]]
      • [[#alert-toast-邮件通知][alert-toast 邮件通知]]
    • [[#elfeed-rss-订阅][elfeed RSS 订阅]]
    • [[#leetcodeel-刷题][leetcode.el 刷题]]
    • [[#latex][LaTex]]
    • [[#emacs-aichat-ai-对话][emacs-aichat AI 对话]]
    • [[#which-key-按键提示][which-key 按键提示]]
    • [[#meow-模式编辑][meow 模式编辑]]
    • [[#启动-emacs-server][启动 emacs server]]
    • [[#载入私有配置文件][载入私有配置文件]]
  • early-init.el :PROPERTIES: :HEADER-ARGS: :tangle early-init.el :END:

Emacs 启动过程的早期加载。

#+begin_src elisp ;;; early-init.el --- early init -- lexical-binding: t no-byte-compile: t; -- #+end_src

** 设置配置启动模式

#+begin_src elisp (defconst my-config-mode (intern (downcase (or (getenv "EMACS_CONFIG_MODE") "full"))))

(defconst full? (eq my-config-mode 'full))

(defconst minimal? (eq my-config-mode 'minimal))

(defconst debug? (eq my-config-mode 'debug))

(defconst not-full? (not full?))

(defconst not-minimal? (not minimal?))

(defconst not-debug? (not debug?)) #+end_src

** 开启 debug-on-error

#+begin_src elisp (when debug? (setq toggle-debug-on-error t)) #+end_src

** 将 gc 的阈值设置到最大,避免启动的时候多次 gc 拖慢启动速度

#+begin_src elisp (setq gc-cons-threshold most-positive-fixnum) #+end_src

** file handler 临时设置为 nil,加快启动速度

#+begin_src emacs-lisp (unless (or (daemonp) noninteractive init-file-debug) (let ((old-file-name-handler-alist file-name-handler-alist)) (setq file-name-handler-alist nil) (add-hook 'emacs-startup-hook (lambda () "Recover file name handlers." (setq file-name-handler-alist (delete-dups (append file-name-handler-alist old-file-name-handler-alist)))))))

#+end_src

** 不要初始化 package

#+begin_src elisp (setq package-enable-at-startup nil) #+end_src

** 禁止 frame 缩放

#+begin_src elisp (setq frame-inhibit-implied-resize t) #+end_src

** 关闭菜单栏

#+begin_src elisp (push '(menu-bar-lines . 0) default-frame-alist) #+end_src

** 关闭工具栏

#+begin_src elisp (push '(tool-bar-lines . 0) default-frame-alist) #+end_src

** 关闭滚动条

#+begin_src elisp (push '(vertical-scroll-bars) default-frame-alist) #+end_src

** 启动后全屏

#+begin_src elisp (when full? (push '(fullscreen . fullscreen) default-frame-alist))

#+end_src

** straight 包管理

#+begin_src elisp (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" (or (bound-and-true-p straight-base-dir) user-emacs-directory))) (bootstrap-version 7)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) #+end_src

** 空闲时增量加载包 from: https://github.com/doomemacs/doomemacs/blob/master/lisp/doom-start.el

#+begin_src emacs-lisp (defvar doom-incremental-packages '(t) "A list of packages to load incrementally after startup. Any large packages here may cause noticeable pauses, so it's recommended you break them up into sub-packages. For example, `org' is comprised of many packages, and can be broken up into:

(doom-load-packages-incrementally
 '(calendar find-func format-spec org-macs org-compat
   org-faces org-entities org-list org-pcomplete org-src
   org-footnote org-macro ob org org-clock org-agenda
   org-capture))

This is already done by the lang/org module, however.

If you want to disable incremental loading altogether, either remove doom-load-packages-incrementally-h' from emacs-startup-hook' or set `doom-incremental-first-idle-timer' to nil. Incremental loading does not occur in daemon sessions (they are loaded immediately at startup).")

(defvar doom-incremental-first-idle-timer (if (daemonp) 0 2.0) "How long (in idle seconds) until incremental loading starts.

Set this to nil to disable incremental loading. Set this to 0 to load all incrementally deferred packages immediately at `emacs-startup-hook'.")

(defvar doom-incremental-idle-timer 0.75 "How long (in idle seconds) in between incrementally loading packages.")

(defun doom-load-packages-incrementally (packages &optional now) "Registers PACKAGES to be loaded incrementally.

If NOW is non-nil, load PACKAGES incrementally, in doom-incremental-idle-timer' intervals." (let ((gc-cons-threshold most-positive-fixnum)) (if (not now) (cl-callf append doom-incremental-packages packages) (while packages (let ((req (pop packages)) idle-time) (if (featurep req) (message "start:iloader: Already loaded %s (%d left)" req (length packages)) (condition-case-unless-debug e (and (or (null (setq idle-time (current-idle-time))) (< (float-time idle-time) doom-incremental-first-idle-timer) (not (while-no-input ;;(message "start:iloader: Loading %s (%d left)" req (length packages)) ;; If default-directory' doesn't exist or is ;; unreadable, Emacs throws file errors. (let ((default-directory user-emacs-directory) (inhibit-message t) (file-name-handler-alist (list (rassq 'jka-compr-handler file-name-handler-alist)))) (require req nil t) t)))) (push req packages)) (error (message "Error: failed to incrementally load %S because: %s" req e) (setq packages nil))) (if (null packages) (message "start:iloader: Finished!") (run-at-time (if idle-time doom-incremental-idle-timer doom-incremental-first-idle-timer) nil #'doom-load-packages-incrementally packages t) (setq packages nil))))))))

(defun doom-load-packages-incrementally-h () "Begin incrementally loading packages in `doom-incremental-packages'.

If this is a daemon session, load them all immediately instead." (when (numberp doom-incremental-first-idle-timer) (if (zerop doom-incremental-first-idle-timer) (mapc #'require (cdr doom-incremental-packages)) (run-with-idle-timer doom-incremental-first-idle-timer nil #'doom-load-packages-incrementally (cdr doom-incremental-packages) t))))

(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h 100)

#+end_src

** use-package 扩展

添加 =:ban= 关键字,不要执行 use-package

#+begin_src elisp (require 'use-package) (push :ban use-package-keywords)

(defalias 'use-package-normalize/:ban 'use-package-normalize-test)

(defalias 'use-package-handler/:ban 'use-package-handler/:unless) #+end_src

** 启动速度测试

#+begin_src elisp (use-package benchmark-init :ban not-debug? :straight t :demand t :hook (after-init . benchmark-init/deactivate) :bind ("" . benchmark-init/show-durations-tree) )

#+end_src

  • emacs 配置 :PROPERTIES: :HEADER-ARGS: :tangle init.el :END:

#+begin_src elisp ;;; init.el --- init -- lexical-binding: t no-byte-compile: t; -- #+end_src

** 载入 custom 文件

#+begin_src elisp (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) (load custom-file t) #+end_src

** 定义一些固定的常量 #+begin_src elisp (defconst sys-is-windows (memq system-type '(cygwin windows-nt ms-dos)))

(defconst sys-is-wsl2 (getenv "WSLENV"))

(defconst my-system-icon (cond (sys-is-wsl2 "  ") ((eq system-type 'gnu/linux) "  ") ((eq system-type 'windows-nt) "  ") ((eq system-type 'darwin) "  " ) (t " ? ")))

(setq global-mode-string (append global-mode-string '(my-system-icon)))

;; 动态库目录 (defconst my-lib-dir (expand-file-name "lib" user-emacs-directory)) (add-to-list 'load-path my-lib-dir)

;; 可执行文件目录 (defconst my-bin-dir (expand-file-name "bin" user-emacs-directory)) ;; 将 my-bin-dir 加入到 PATH 中 (setenv "PATH" (concat my-bin-dir (if sys-is-windows ";" ":") (getenv "PATH")))

;; 外部配置文件目录 (defconst my-etc-dir (expand-file-name "etc" user-emacs-directory))

;; 存放代码的目录 (if sys-is-windows (defconst my-code-dir (expand-file-name "D:/Code")) (defconst my-code-dir (expand-file-name "~/Code")))

;; 开源代码 (defconst my-code-opensource-dir (expand-file-name "Github" my-code-dir))

;; 工作代码 (defconst my-code-work-dir (expand-file-name "Work" my-code-dir))

;; 个人项目代码 (defconst my-code-project-dir (expand-file-name "Project" my-code-dir))

;; 练习代码或实例代码,这里面一般根据语言再分一层 (defconst my-code-demo-dir (expand-file-name "Demo" my-code-dir))

;; 数据目录 (if sys-is-windows (defconst my-docs-dir (expand-file-name "D:/Docs")) (if sys-is-wsl2 (defconst my-docs-dir (expand-file-name "/mnt/d/Docs")) (defconst my-docs-dir (expand-file-name "~/Docs"))))

;; 笔记目录 (defconst my-notes-dir (expand-file-name "Notes" my-docs-dir))

;; 博客相关目录 (if sys-is-windows (defconst my-blogs-dir (expand-file-name "D:/BaiduSyncdisk/Blogs")) (if sys-is-wsl2 (defconst my-blogs-dir (expand-file-name "/mnt/d/BaiduSyncdisk/Blogs")) (defconst my-blogs-dir (expand-file-name "~/BaiduSyncdisk/Blogs")))) (defconst my-blogs-org-dir (expand-file-name "org" my-blogs-dir)) (defconst my-blogs-image-dir (expand-file-name "images" my-blogs-dir))

;; Org 相关目录 (if sys-is-windows (defconst my-org-dir (expand-file-name "Org" my-docs-dir)) (if sys-is-wsl2 (defconst my-org-dir (expand-file-name "Org" my-docs-dir)) (defconst my-org-dir (expand-file-name "Org" my-docs-dir)))) ;; 私有文件目录 (defconst my-private-dir (expand-file-name "Private" my-org-dir))

;; 私有代码片段目录 (defconst my-private-snippets-dir (expand-file-name "snippets" my-private-dir))

#+end_src

** 常用的工具函数

*** 重命名当前文件

#+begin_src elisp (defun my/rename-this-file-and-buffer (new-name) "Rename both current buffer and file it's visiting to NEW_NAME." (interactive "sNew name: ") (let ((name (buffer-name)) (filename (buffer-file-name))) (unless filename (error "Buffer '%s' is not visiting a file" name)) (progn (when (file-exists-p filename) (rename-file filename new-name 1)) (set-visited-file-name new-name) (rename-buffer new-name)))) #+end_src

*** 删除当前文件

#+begin_src emacs-lisp (defun my/delete-file-and-buffer () "Kill the current buffer and deletes the file it is visiting." (interactive) (let ((filename (buffer-file-name))) (when (and filename (y-or-n-p (concat "Do you really want to delete file " filename "?"))) (delete-file filename t) (message "Deleted file %s." filename) (kill-buffer) ))) #+end_src

*** 清理 Message buffer

#+begin_src elisp (defun my/clear-messages-buffer () "Clear Messages buffer." (interactive) (let ((inhibit-read-only t)) (with-current-buffer "Messages" (erase-buffer)))) #+end_src

*** 开关网络代理

#+begin_src elisp (defun my/toggle-url-proxy () "Toggle proxy for the url.el library." (interactive) (require 'url) (cond (url-proxy-services (message "Turn off URL proxy") (setq url-proxy-services nil)) (t (message "Turn on URL proxy") (setq url-proxy-services '(("http" . "127.0.0.1:7890") ("https" . "127.0.0.1:7890") ("no_proxy" . "0.0.0.0"))))))

#+end_src

*** 清理掉没用的 buffer

#+begin_src elisp (defun my/kill-unused-buffers () "Kill unused buffers." (interactive) (ignore-errors (save-excursion (dolist (buf (buffer-list)) (set-buffer buf) (when (and (string-prefix-p "" (buffer-name)) (string-suffix-p "" (buffer-name))) (kill-buffer buf)) )))) #+end_src

** 基本设置 *** 用户信息

#+begin_src elisp (setq user-full-name "xhcoding" user-mail-address "[email protected]") #+end_src

*** 增加 IO 性能

#+begin_src elisp (setq process-adaptive-read-buffering nil) (setq read-process-output-max (* 1024 1024)) #+end_src

*** 设置编码

默认使用 utf-8 ,在 windows 文件名编码使用 gbk ,不然打不开中文文件 #+begin_src elisp (prefer-coding-system 'utf-8) (when sys-is-windows (setq file-name-coding-system 'gbk)) #+end_src

*** 设置 frame 标题

#+begin_src emacs-lisp (setq frame-title-format "Emacs") #+end_src

*** 关闭启动动画

#+begin_src elisp (setq inhibit-startup-message t) #+end_src

*** scratch buffer 设置

scratch 为 fundaemental-mode

#+begin_src elisp (setq initial-major-mode 'fundamental-mode) #+end_src

scratch buffer 内容为空

#+begin_src elisp (setq initial-scratch-message nil) #+end_src

*** yes-or-no-p 改为 y-or-n-p

#+begin_src elisp (setq use-short-answers t) #+end_src

*** 关掉提示

#+begin_src elisp (setq ring-bell-function 'ignore) #+end_src

*** 禁止光标闪烁

#+begin_src elisp (blink-cursor-mode -1) #+end_src

*** 启动时不要显示 For information about GNU Emacs...

#+begin_src elisp (advice-add #'display-startup-echo-area-message :override #'ignore) #+end_src

*** tab 设置

#+begin_src elisp (setq-default tab-width 4)

(setq-default indent-tabs-mode nil) #+end_src

*** 一行不超过 120 字符

#+begin_src elisp (setq-default fill-column 120) (column-number-mode +1) #+end_src

*** 删除文件时放到回收站

#+begin_src elisp (setq-default delete-by-moving-to-trash t) #+end_src

*** 高亮当前行

#+begin_src elisp (global-hl-line-mode +1) #+end_src

** gcmh 优化垃圾回收

#+begin_src elisp (use-package gcmh :straight t :hook (emacs-startup . gcmh-mode) :init (setq gcmh-idle-delay 'auto gcmh-auto-idle-delay-factor 10 gcmh-high-cons-threshold 33554432)) ; 32MB #+end_src

** auto-save 自动保存 ref: https://github.com/manateelazycat/auto-save

#+begin_src elisp (use-package auto-save :straight (auto-save :type git :host github :repo "manateelazycat/auto-save") :defer 3 :init ;; 关闭 emacs 默认的自动备份 (setq make-backup-files nil) ;; 关闭 emacs 默认的 自动保存 (setq auto-save-default nil) :config (setq auto-save-silent t) (auto-save-enable) )

#+end_src

** rime 智能的中文输入法

#+begin_src elisp (use-package rime :straight t :defer t :bind ("C-j" . rime-force-enable) :init (when sys-is-windows (setq rime--module-path (expand-file-name (concat "librime-emacs" module-file-suffix) my-lib-dir)) (setq rime-share-data-dir (expand-file-name "rime-data" my-etc-dir)))

(defun my-*require-rime(&rest _)
  "Require rime when toggle-input-method."
  (unless (featurep 'rime)
    (require 'rime)))

(advice-add 'toggle-input-method :before #'my-*require-rime)

:custom
(default-input-method "rime")
(rime-user-data-dir (expand-file-name "rime-user" my-etc-dir))

:config
(setq
 rime-disable-predicates '(rime-predicate-after-alphabet-char-p
                           rime-predicate-prog-in-code-p))
(if (display-graphic-p)
    (setq rime-show-candidate 'posframe)
  (setq rime-show-candidate 'minibuffer)))

#+end_src

** 字体和主题

#+begin_src elisp (defun my-font-installed-p (font-name) "Check if font with FONT-NAME is available." (find-font (font-spec :name font-name)))

(defun my-setup-fonts () "Setup fonts." (when (display-graphic-p) ;; Set default font (cl-loop for font in '("CaskaydiaCove NFM" "Fira Code" "Jetbrains Mono" "SF Mono" "Hack" "Source Code Pro" "Menlo" "Monaco" "DejaVu Sans Mono" "Consolas") when (my-font-installed-p font) return (set-face-attribute 'default nil :family font :height 120))

  ;; Specify font for all unicode characters
  (cl-loop for font in '("Segoe UI Emoji" "Apple Symbols" "Symbola" "Symbol")
           when (my-font-installed-p font)
           return (set-fontset-font t 'symbol (font-spec :family font) nil 'prepend))

  ;; Emoji
  (cl-loop for font in '("Segoe UI Emoji" "Noto Color Emoji" "Apple Color Emoji")
           when (my-font-installed-p font)
           return (set-fontset-font t
                                    (if (< emacs-major-version 28)'symbol 'emoji)
                                    (font-spec :family font) nil 'prepend))

  ;; Specify font for Chinese characters
  (cl-loop for font in '("微软雅黑" "WenQuanYi Micro Hei Mono")
           when (my-font-installed-p font)
           return (set-fontset-font t 'han (font-spec :family font)))))

(my-setup-fonts) (add-hook 'window-setup-hook #'my-setup-fonts) (add-hook 'server-after-make-frame-hook #'my-setup-fonts)

(use-package modus-themes :straight (:type git :host github :repo "protesilaos/modus-themes") )

(use-package ef-themes :straight (:type git :host github :repo "protesilaos/ef-themes") :config (modus-themes-load-theme 'ef-owl) )

(use-package nerd-icons :straight t :init (cl-loop for font in '("CaskaydiaCove NFM" "Symbols Nerd Font Mono") when (my-font-installed-p font) return (setq nerd-icons-font-family font))) #+end_src

** ligature 连字体

#+begin_src elisp (use-package ligature :ban minimal? :straight t :defer t :hook prog-mode :config ;; Enable all Cascadia Code ligatures in programming modes (ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "" "---" "-<<" "<~~" "<~>" "<>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#(" "..<" "..." "+++" "/==" "///" "|_" "www" "&&" "^=" "~~" "~@" "~=" "~>" "~-" "**" ">" "/" "||" "|}" "|]" "|=" "|>" "|-" "{|" "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<" "<|" "<:" "<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!" "##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:" "?=" "?." "??" ";;" "/" "/=" "/>" "//" "__" "~~" "(" "*)" "\\" "://"))) #+end_src

** doom-modeline

#+begin_src elisp (use-package doom-modeline :straight t :hook (after-init . doom-modeline-mode)) #+end_src

** hydra 方便的快捷键设置

#+begin_src elisp (use-package hydra :straight t)

(use-package pretty-hydra :straight t :after hydra :init (cl-defun pretty-hydra-title (title &optional icon-type icon-name &key face height v-adjust) "Add an icon in the hydra title." (let ((face (or face `(:foreground ,(face-background 'highlight)))) (height (or height 1.0)) (v-adjust (or v-adjust 0.0))) (concat (when (and icon-type icon-name) (let ((f (intern (format "all-the-icons-%s" icon-type)))) (when (fboundp f) (concat (apply f (list icon-name :face face :height height :v-adjust v-adjust)) " ")))) (propertize title 'face face)))))

#+end_src

** recentf 读取最近文件

#+begin_src elisp (use-package recentf :commands (recentf-open-files) :hook (after-init . recentf-mode) :bind ("C-x C-r" . recentf-open-files) :init (setq recentf-max-saved-items 500 recentf-exclude '("\.?cache" ".cask" "url" "COMMIT_EDITMSG\'" "bookmarks" "\.\(?:gz\|gif\|svg\|png\|jpe?g\|bmp\|xpm\)$" "\.?ido\.last$" "\.revive$" "/G?TAGS$" "/.elfeed/" "^/tmp/" "^/var/folders/.+$" ; "^/ssh:" (lambda (file) (file-in-directory-p file package-user-dir)))) :config (push (expand-file-name recentf-save-file) recentf-exclude))

#+end_src

** popper 更方便的弹出窗口

#+begin_src elisp (use-package popper :straight t :hook (emacs-startup . popper-mode) :init (setq popper-reference-buffers '( help-mode rustic-cargo-run-mode lsp-bridge-ref-mode "^\eshell.\$" eshell-mode ;; emacs-aichat "^\?[aA]ichat.\$"

        "^\\*xref*\\*$"
        "^\\*compilation*\\*$"
        ))
:config

(with-no-warnings
  (defun my-popper-fit-window-height (win)
    "Determine the height of popup window WIN by fitting it to the buffer's content."
    (fit-window-to-buffer
     win
     (floor (frame-height) 3)
     (floor (frame-height) 3)))
  (setq popper-window-height #'my-popper-fit-window-height)

  (defun my-popper-window-popper-p (buffer)
    "Whether `buffer' is popper window."
    (when-let* ((window (caar popper-open-popup-alist))
                (buffer (cdar popper-open-popup-alist))
                (window-p (string= (buffer-name) (buffer-name buffer))))
      window))

  (defun my-popper-close-window (&rest _)
    "Close popper window via `C-g'."
    ;; `C-g' can deactivate region
    (when (and (called-interactively-p 'interactive)
               (not (region-active-p))
               popper-open-popup-alist)
      (let ((window (caar popper-open-popup-alist)))
        (when (window-live-p window)
          (delete-window window)))))

  (advice-add #'keyboard-quit :before #'my-popper-close-window))
)

#+end_src

** autorevert 自动同步外部文件改变

#+begin_src elisp (use-package autorevert :config (global-auto-revert-mode +1))

#+end_src

** minibuffer 补全及增强

#+begin_src elisp (use-package pinyinlib :straight t :defer t)

(use-package orderless :straight t :custom (completion-styles '(orderless)) :config ;; 拼音搜索支持 (defun completion--regex-pinyin (str) (require 'pinyinlib) (orderless-regexp (pinyinlib-build-regexp-string str))) (add-to-list 'orderless-matching-styles 'completion--regex-pinyin) )

(use-package vertico :straight t :config (vertico-mode +1))

(use-package marginalia :after (vertico) :straight t :config (marginalia-mode +1))

(use-package consult :straight t :defer t :bind (("C-s" . consult-line) ("C-x b" . consult-buffer) ("C-x C-b" . consult-bookmark) ("C-x C-i" . consult-imenu)) :custom (consult-preview-key nil) (consult-buffer-sources '(consult--source-buffer consult--source-recent-file)) :config

(when sys-is-windows
  (add-to-list 'process-coding-system-alist '("es.exe" gbk . gbk))
  (add-to-list 'process-coding-system-alist '("explorer" gbk . gbk))
  (add-to-list 'process-coding-system-alist '("rg" utf-8 . gbk))
  (setq consult-locate-args (encode-coding-string "es.exe -i -p -r" 'gbk))))

#+end_src

** vundo undo 增强

#+begin_src emacs-lisp (use-package vundo :straight t :bind ("C-x u" . vundo)) #+end_src

** avy 快速移动光标到屏幕上任意字符

#+begin_src elisp (use-package avy :straight t :bind (("M-'" . my/avy-goto-char-timer)) :init (defun my/avy-goto-char-timer (&optional arg) "Make avy-goto-char-timer support pinyin" (interactive "P") (require 'pinyinlib) (require 'avy) (let ((avy-all-windows (if arg (not avy-all-windows) avy-all-windows))) (avy-with avy-goto-char-timer (setq avy--old-cands (avy--read-candidates 'pinyinlib-build-regexp-string)) (avy-process avy--old-cands))))

:config
(setq avy-all-windows nil
      avy-all-windows-alt t
      avy-background t
      avy-style 'pre))

#+end_src

** ace-window 快速切换到其它 window

#+begin_src elisp (use-package ace-window :straight t :pretty-hydra ((:title (pretty-hydra-title "Window Management" 'faicon "th" :height 1.1 :v-adjust -0.1) :foreign-keys warn :quit-key ("q" "C-g")) ("Split" (("r" split-window-right "horizontally" :exit t) ("R" split-window-right "horizontally continue") ("v" split-window-below "vertically" :exit t) ("V" split-window-below "vertically continue"))

  "Resize"
  (("h" shrink-window-horizontally "←")
   ("j" enlarge-window "↓")
   ("k" shrink-window "↑")
   ("l" enlarge-window-horizontally "→")
   ("n" balance-windows "balance" :exit t))

  "Zoom"
  (("+" text-scale-increase "in")
   ("=" text-scale-increase "in")
   ("-" text-scale-decrease "out")
   ("0" (text-scale-increase 0) "reset"))))
  :bind (("M-o" . ace-window)
         ("C-c w" . ace-window-hydra/body))

)

#+end_src

** toggle-one-window 快速切换到单窗口

ref:https://github.com/manateelazycat/toggle-one-window

#+begin_src elisp (defvar my-window--configuration nil "The window configuration use for `toggle-one-window'.")

(defun my-window--one-window-p () (equal 1 (length (cl-remove-if #'(lambda (w) (and (window-dedicated-p w) (not (window-parameter w 'quit-restore)))) (window-list)))))

(defun my/toggle-one-window () "Toggle between window layout and one window." (interactive) (cond ;; 如果当前 buffer 所在 Window 是 popper ((my-popper-window-popper-p (current-buffer)) (if (my-window--one-window-p) (when my-window--configuration (set-window-configuration my-window--configuration) (setq my-window--configuration nil))

    (setq my-window--configuration (current-window-configuration))
    (let ((buffer (current-buffer)))
      (other-window 1)
      (delete-other-windows)
      (switch-to-buffer buffer))))
 (t
  (if (my-window--one-window-p)
      (when my-window--configuration
        (set-window-configuration my-window--configuration)
        (setq my-window--configuration nil))
    (setq my-window--configuration (current-window-configuration))
    (delete-other-windows)))))

(global-set-key (kbd "M-;") #'my/toggle-one-window) #+end_src

** yasnippet 快速插入代码片段

#+begin_src elisp (use-package yasnippet :straight t :defer t :config (when (file-directory-p my-private-snippets-dir) (add-to-list 'yas-snippet-dirs my-private-snippets-dir)) (yas-reload-all))

(use-package yasnippet-snippets :straight t :after yasnippet)

#+end_src

** separedit 快速编辑

#+begin_src elisp (use-package separedit :straight t :bind ("C-c '" . separedit)) #+end_src

** compile 增强

#+begin_src elisp (use-package fancy-compilation :straight t :after compile :config (setq fancy-compilation-override-colors nil) (fancy-compilation-mode +1)) #+end_src

** eshell 增强

*** eshell 基本配置 #+begin_src elisp (use-package eshell :disabled :defer t :custom (eshell-kill-processes-on-exit t) :config

(my-cc--import-vcvars)
(setq exec-path (parse-colon-path (getenv "Path")))

;; 默认为插入模式
(add-to-list 'meow-mode-state-list '(eshell-mode . insert))


;; 配合 popper 实现 toggle 效果
(defun my/eshell ()
  (interactive)
  (if-let* ((window (caar popper-open-popup-alist))
           (buffer (cdar popper-open-popup-alist))
           (eshell-opened (string= eshell-buffer-name (buffer-name buffer))))
      (when (window-live-p window)
        (delete-window window))
    (eshell)))

;; cat 高亮
(defun my-eshell-cat-with-syntax-highlight (file)
  "Like cat but with syntax highlight."
  (with-temp-buffer
    (insert-file-contents file)
    (let ((buffer-file-name file))
      (delay-mode-hooks
        (set-auto-mode)
        (font-lock-ensure)))
    (buffer-string)))

(advice-add 'eshell/cat :override #'my-eshell-cat-with-syntax-highlight)

)

#+end_src

*** eshell 历史记录设置

#+begin_src elisp (use-package em-hist :disabled :defer t :custom (eshell-history-size 10240) (eshell-hist-ignoredups t) (eshell-save-history-on-exit t)) #+end_src

*** esh-mode 设置

#+begin_src elisp (use-package esh-mode :disabled :bind (:map eshell-mode-map ("C-r" . consult-history))) #+end_src

*** eshell-git-prompt 提示符美化

#+begin_src elisp (use-package eshell-git-prompt :disabled :straight t :after esh-mode :config (eshell-git-prompt-use-theme 'powerline)) #+end_src

*** eshell-z 智能目录跳转

#+begin_src elisp (use-package eshell :disabled :defer t :config (require 'em-dirs) (defvar my-eshell-z--table nil)

(defvar my-eshell-z-file-name (expand-file-name "z" eshell-directory-name))

(defun my-eshell-z--load ()
  (setq my-eshell-z--table (make-hash-table :test 'equal))
  (when (file-exists-p my-eshell-z-file-name)
    (dolist (element (with-temp-buffer
                       (insert-file-contents my-eshell-z-file-name)
                       (goto-char (point-min))
                       (read (current-buffer))))
      (when (file-directory-p (car element))
        (puthash (car element) (cadr element) my-eshell-z--table)))))

(defun my-eshell-z--save ()
  (let ((dir (file-name-directory my-eshell-z-file-name)))
    (unless (file-exists-p dir)
      (make-directory dir t))
    (with-temp-file my-eshell-z-file-name
      (let ((result (list)))
        (maphash #'(lambda (key value)
                     (when (> value 0)
                       (add-to-list 'result (list key (- value 0.1))))
                     )
                 my-eshell-z--table)
        (prin1 result (current-buffer))))))

(defun my-eshell-z--update ()
  (let ((cur-dir default-directory))
    (if-let ((score (gethash cur-dir my-eshell-z--table)))
        (puthash cur-dir (+ score 1) my-eshell-z--table)
      (puthash cur-dir 1 my-eshell-z--table))))

(defun eshell/z (&rest args)
  (let* ((first (car args))
         (result first))
    (if (not first)
        (setq result "~")
      (cond
       ((string-match-p "^[\\.]+$" first)
        (let ((target ""))
          (cl-loop repeat (length first) do
                   (setq target (concat target "../")))
          (setq result target)))
       ((string= "-" first)
        (setq result first))
       (t (let ((regex "")
                target-score
                target-dir)
            (dolist (arg args)
              (setq regex (concat regex arg ".*")))
            (maphash #'(lambda (key value)
                         (when (string-match-p regex key)
                           (when (and target-score (> value target-score))
                             (setq target-dir key
                                   target-score value))
                           (unless target-score
                             (setq target-dir key
                                   target-score value))))
                     my-eshell-z--table)
            (if target-dir
                (setq result target-dir)
              (setq result args))
            ))))
    ;; (message "result: %s" result)
    (eshell/cd result)))
(add-hook 'eshell-mode-hook #'my-eshell-z--load)
(add-hook 'eshell-directory-change-hook #'my-eshell-z--update)
(add-hook 'kill-emacs-hook #'my-eshell-z--save)

(defun eshell/zp (&rest args)
  "Jump directory in current project."
  (let* ((project-root (nth 2 (project-current))))
    (unless project-root
      (setq project-root default-directory))
    (when-let* ((result (eshell-command-result
                        (concat "fd --type directory --absolute-path " (car args) " " project-root)))
                (paths (split-string result "\n" t)))
      (if (= (length paths) 1)
          (eshell/cd (car paths))
        (eshell/cd (completing-read "Choose: " paths nil t))))))
)

#+end_src

** emacs-libvterm #+begin_src elisp (use-package vterm :straight (vterm :type git :host github :repo "xhcoding/emacs-libvterm") :preface (defun my-vterm-setup() (setq-local global-hl-line-mode nil) (meow-mode -1)) :hook (vterm-mode . my-vterm-setup) :config (setq vterm-shell "pwsh.exe") (defun my-project-shell () "Start an inferior shell in the current project's root directory. If a buffer already exists for running a shell in the project's root, switch to it. Otherwise, create a new shell buffer. With \[universal-argument] prefix arg, create a new inferior shell buffer even if one already exists." (interactive) (require 'comint) (let* ((default-directory (project-root (project-current t))) (default-project-shell-name (project-prefixed-buffer-name "shell")) (shell-buffer (get-buffer default-project-shell-name))) (if (and shell-buffer (not current-prefix-arg)) (if (comint-check-proc shell-buffer) (pop-to-buffer shell-buffer (bound-and-true-p display-comint-buffer-action)) (vterm shell-buffer)) (vterm (generate-new-buffer-name default-project-shell-name)))))

(advice-add 'project-shell :override #'my-project-shell)


(defvar my-vterm-shells
'(("*vterm-pwsh*"    . "pwsh.exe")
  ("*vterm-ros2*" . "wsl.exe -d ubuntu --cd ~ -e bash -c -l \"source /opt/ros/jazzy/setup.bash && exec bash -l\""))
"Alist of (BUFFER-NAME . SHELL-COMMAND) for vterm shells.")

(defun my/vterm-select-or-create ()
  "Select an existing vterm, or create one from a shell or custom name."
  (interactive)
  (let* ((existing-vterms
          (mapcar #'buffer-name
                  (seq-filter (lambda (buf)
                                (with-current-buffer buf
                                  (eq major-mode 'vterm-mode)))
                              (buffer-list))))
         (shell-names (mapcar #'car my-vterm-shells))
         (candidates (append shell-names existing-vterms))
         (chosen (completing-read "VTerm (select or type new name): "
                                  existing-vterms nil nil "")))
    (when (string-empty-p chosen)
      (setq chosen (completing-read "Select shells: " shell-names nil nil "")))
    
    (if (get-buffer chosen)
        (switch-to-buffer chosen)
      (let ((vterm-shell (cdr (assoc chosen my-vterm-shells))))
        (vterm chosen)))))

)

#+end_src ** fingertip 智能括号插入

#+begin_src emacs-lisp (use-package fingertip :straight (fingertip :type git :host github :repo "manateelazycat/fingertip") :defer t :hook ((prog-mode toml-ts-mode) . my-enable-pair-parents) :bind (:map fingertip-mode-map ("(" . fingertip-open-round) ("[" . fingertip-open-bracket) ("{" . fingertip-open-curly) (")" . fingertip-close-round) ("]" . fingertip-close-bracket) ("}" . fingertip-close-curly) ("=" . fingertip-equal) (""" . fingertip-double-quote) ("SPC" . fingertip-space) ("RET". fingertip-newline) ("C-k" . fingertip-kill) ("M-"" . fingertip-wrap-double-quote) ("M-[" . fingertip-wrap-bracket) ("M-{" . fingertip-wrap-curly) ("M-(" . fingertip-wrap-round) ("M-]" . fingertip-unwrap) ("M-n" . fingertip-jump-right) ("M-p" . fingertip-jump-left) ("M-RET" . fingertip-jump-out-pair-and-newline)) :init (defun my-enable-pair-parents () (if (treesit-parser-list) (fingertip-mode) (electric-pair-mode)))) #+end_src

** color-rg 快速搜索重构

#+begin_src elisp (use-package color-rg :straight (color-rg :type git :host github :repo "manateelazycat/color-rg") :defer t :commands (color-rg-search-symbol-in-project color-rg-search-input-in-project) :custom (color-rg-search-no-ignore-file nil) :config (add-to-list 'meow-mode-state-list '(color-rg-mode . motion))) #+end_src

** markdown

#+begin_src elisp (use-package markdown-mode :straight t :defer t)

#+end_src

** lsp-bridge 代码补全

#+begin_src elisp

(use-package lsp-bridge :straight (lsp-bridge :type git :host github :repo "manateelazycat/lsp-bridge" :files ("*") :build nil) :defer t :bind (:map lsp-bridge-mode-map ([remap xref-find-definitions] . lsp-bridge-find-def) ([remap xref-find-references] . lsp-bridge-find-references) ([remap xref-go-back] . lsp-bridge-find-def-return)) :init

;; 手动添加到 load-path
(add-to-list 'load-path (straight--repos-dir "lsp-bridge"))

(setq lsp-bridge-org-babel-lang-list nil)

;; https://tecosaur.github.io/emacs-config/config.html#lsp-support-src
(cl-defmacro my-lsp-org-babel-enable (lang)
  "Support LANG in org source code block."
  (cl-check-type lang string)
  (let* ((edit-pre (intern (format "org-babel-edit-prep:%s" lang)))
         (intern-pre (intern (format "lsp--%s" (symbol-name edit-pre)))))
    `(progn
       (defun ,intern-pre (info)
         (let ((file-name (->> info caddr (alist-get :file))))
           (unless file-name
             (setq file-name (expand-file-name "OrgBabel/org-src-babel" my-code-dir))
             (write-region (point-min) (point-max) file-name))
           (setq buffer-file-name file-name)
           (my-enable-code-service)))
       (put ',intern-pre 'function-documentation
            (format "Enable lsp-bridge-mode in the buffer of org source block (%s)."
                    (upcase ,lang)))
       (if (fboundp ',edit-pre)
           (advice-add ',edit-pre :after ',intern-pre)
         (progn
           (defun ,edit-pre (info)
             (,intern-pre info))
           (put ',edit-pre 'function-documentation
                (format "Prepare local buffer environment for org source block (%s)."
                        (upcase ,lang))))))))

(with-eval-after-load 'org
  (dolist (lang '("c" "cpp" "python" "rust"))
    (eval `(my-lsp-org-babel-enable ,lang))))


:config
(push '(scss-mode . "vscode-css-language-server") lsp-bridge-single-lang-server-mode-list)
(push '(json-ts-mode . "vscode-json-language-server") lsp-bridge-single-lang-server-mode-list)

(setq lsp-bridge-user-langserver-dir my-etc-dir
      lsp-bridge-user-multiserver-dir my-etc-dir)

(setq lsp-bridge-cmake-lsp-server "neocmakelsp")

(setq acm-enable-tabnine nil
      acm-enable-quick-access t
      lsp-bridge-enable-hover-diagnostic t)
)

#+end_src

** acm-terminal

#+begin_src elisp (use-package popon :straight t :defer t) (use-package acm-terminal :defer t :straight (acm-terminal :type git :host github :repo "twlz0ne/acm-terminal") :init (unless (display-graphic-p) (with-eval-after-load 'acm (require 'acm-terminal)))) #+end_src

** company 补全

#+begin_src elisp (use-package company :straight t :defer t :bind (:map company-active-map ("TAB" . company-complete-selection))) #+end_src

** eglot-booster 加速 eglot

#+begin_src elisp (use-package eglot-booster :straight (eglot-booster :type git :host github :repo "jdtsmith/eglot-booster") :after eglot :config (eglot-booster-mode)) #+end_src

** 启动代码服务

#+begin_src elisp (defun my-enable-code-service () (require 'yasnippet) (yas-minor-mode) (if (getenv "EMACS_USE_EGLOT") (progn (company-mode) (eglot-ensure) ) (require 'lsp-bridge) (lsp-bridge-mode))) #+end_src

** highlight-parentheses 高亮外层括号

#+begin_src elisp (use-package highlight-parentheses :straight t :hook (prog-mode . highlight-parentheses-mode)) #+end_src

** evil-nerd-commenter 智能注释代码

#+begin_src elisp (use-package evil-nerd-commenter :straight t :bind ("C-/" . evilnc-comment-or-uncomment-lines)) #+end_src

** apheleia 智能格式化代码

#+begin_src elisp (use-package apheleia :straight t :defer t) #+end_src

** git 配置

*** magit

#+begin_src elisp (use-package magit :straight (magit :type git :host github :repo "magit/magit" :files ("lisp/magit" "lisp/magit*.el" "lisp/git-rebase.el" "lisp/git-commit.el" "docs/magit.texi" "docs/AUTHORS.md" "LICENSE" "Documentation/magit.texi" ; temporarily for stable "Documentation/AUTHORS.md" ; temporarily for stable )) :bind ("C-x g" . magit-status) :config (when sys-is-windows (setenv "GIT_ASKPASS" "git-gui--askpass")))

(use-package ssh-agency :straight t :after magit)

#+end_src

*** 显示当前行的最后提交信息

#+begin_src emacs-lisp (use-package git-messenger :straight t :bind (:map vc-prefix-map ("p" . git-messenger:popup-message) :map git-messenger-map ("m" . git-messenger:copy-message)) :init (setq git-messenger:show-detail t git-messenger:use-magit-popup t)

(defface posframe-border
  `((t (:inherit region)))
  "Face used by the `posframe' border."
  :group 'posframe)
:config
(with-no-warnings
  (with-eval-after-load 'hydra
    (defhydra git-messenger-hydra (:color blue)
      ("s" git-messenger:popup-show "show")
      ("c" git-messenger:copy-commit-id "copy hash")
      ("m" git-messenger:copy-message "copy message")
      ("," (catch 'git-messenger-loop (git-messenger:show-parent)) "go parent")
      ("q" git-messenger:popup-close "quit")))

  (defun my-git-messenger:format-detail (vcs commit-id author message)
    (if (eq vcs 'git)
        (let ((date (git-messenger:commit-date commit-id))
              (colon (propertize ":" 'face 'font-lock-comment-face)))
          (concat
           (format "%s%s %s \n%s%s %s\n%s  %s %s \n"
                   (propertize "Commit" 'face 'font-lock-keyword-face) colon
                   (propertize (substring commit-id 0 8) 'face 'font-lock-comment-face)
                   (propertize "Author" 'face 'font-lock-keyword-face) colon
                   (propertize author 'face 'font-lock-string-face)
                   (propertize "Date" 'face 'font-lock-keyword-face) colon
                   (propertize date 'face 'font-lock-string-face))
           (propertize (make-string 38 ?─) 'face 'font-lock-comment-face)
           message
           (propertize "\nPress q to quit" 'face '(:inherit (font-lock-comment-face italic)))))
      (git-messenger:format-detail vcs commit-id author message)))

  (defun my-git-messenger:popup-message ()
    "Popup message with `posframe', `pos-tip', `lv' or `message', and dispatch actions with `hydra'."
    (interactive)
    (let* ((hydra-hint-display-type 'message)
           (vcs (git-messenger:find-vcs))
           (file (buffer-file-name (buffer-base-buffer)))
           (line (line-number-at-pos))
           (commit-info (git-messenger:commit-info-at-line vcs file line))
           (commit-id (car commit-info))
           (author (cdr commit-info))
           (msg (git-messenger:commit-message vcs commit-id))
           (popuped-message (if (git-messenger:show-detail-p commit-id)
                                (my-git-messenger:format-detail vcs commit-id author msg)
                              (cl-case vcs
                                (git msg)
                                (svn (if (string= commit-id "-")
                                         msg
                                       (git-messenger:svn-message msg)))
                                (hg msg)))))
      (setq git-messenger:vcs vcs
            git-messenger:last-message msg
            git-messenger:last-commit-id commit-id)
      (run-hook-with-args 'git-messenger:before-popup-hook popuped-message)
      (git-messenger-hydra/body)
      (cond ((and (fboundp 'posframe-workable-p) (posframe-workable-p))
             (let ((buffer-name "*git-messenger*"))
               (posframe-show buffer-name
                              :string (concat (propertize "\n" 'face '(:height 0.3))
                                              popuped-message
                                              "\n"
                                              (propertize "\n" 'face '(:height 0.3)))
                              :left-fringe 8
                              :right-fringe 8
                              :max-width (round (* (frame-width) 0.62))
                              :max-height (round (* (frame-height) 0.62))
                              :internal-border-width 1
                              :internal-border-color (face-background 'posframe-border nil t)
                              :background-color (face-background 'tooltip nil t))
               (unwind-protect
                   (push (read-event) unread-command-events)
                 (posframe-hide buffer-name))))
            ((and (fboundp 'pos-tip-show) (display-graphic-p))
             (pos-tip-show popuped-message))
            ((fboundp 'lv-message)
             (lv-message popuped-message)
             (unwind-protect
                 (push (read-event) unread-command-events)
               (lv-delete-window)))
            (t (message "%s" popuped-message)))
      (run-hook-with-args 'git-messenger:after-popup-hook popuped-message)))
  (advice-add #'git-messenger:popup-close :override #'ignore)
  (advice-add #'git-messenger:popup-message :override #'my-git-messenger:popup-message)))

#+end_src

*** smerge-mode 解决冲突

#+begin_src elisp (use-package smerge-mode :ensure nil :pretty-hydra ((:title (pretty-hydra-title "Smerge" 'octicon "diff") :color pink :quit-key "q") ("Move" (("n" smerge-next "next") ("p" smerge-prev "previous")) "Keep" (("b" smerge-keep-base "base") ("u" smerge-keep-upper "upper") ("l" smerge-keep-lower "lower") ("a" smerge-keep-all "all") ("RET" smerge-keep-current "current") ("C-m" smerge-keep-current "current")) "Diff" (("<" smerge-diff-base-upper "upper/base") ("=" smerge-diff-upper-lower "upper/lower") (">" smerge-diff-base-lower "upper/lower") ("R" smerge-refine "refine") ("E" smerge-ediff "ediff")) "Other" (("C" smerge-combine-with-next "combine") ("r" smerge-resolve "resolve") ("k" smerge-kill-current "kill") ))) :bind (:map smerge-mode-map ("C-c m" . smerge-mode-hydra/body))) #+end_src

** visual-regexp 可视化的正则替换

#+begin_src elisp (use-package visual-regexp :straight t :defer t) #+end_src

** expand-region 递增选区

#+begin_src emacs-lisp (use-package expand-region :straight t :bind (("C-=" . er/expand-region)) :config (defun treesit-mark-bigger-node () (let* ((root (treesit-buffer-root-node)) (node (treesit-node-descendant-for-range root (region-beginning) (region-end))) (node-start (treesit-node-start node)) (node-end (treesit-node-end node))) ;; Node fits the region exactly. Try its parent node instead. (when (and (= (region-beginning) node-start) (= (region-end) node-end)) (when-let ((node (treesit-node-parent node))) (setq node-start (treesit-node-start node) node-end (treesit-node-end node)))) (set-mark node-end) (goto-char node-start)))

(add-to-list 'er/try-expand-list 'treesit-mark-bigger-node)
)

#+end_src

** 启用 treesit

#+begin_src elisp (setq major-mode-remap-alist '((c-mode . c-ts-mode) (c++-mode . c++-ts-mode) (c-or-c++-mode . c-or-c++-ts-mode) (cmake-mode . cmake-ts-mode) (conf-toml-mode . toml-ts-mode) (csharp-mode . csharp-ts-mode) (css-mode . css-ts-mode) (dockerfile-mode . dockerfile-ts-mode) (go-mode . go-ts-mode) (java-mode . java-ts-mode) (json-mode . json-ts-mode) (js-json-mode . json-ts-mode) (javascript-mode . js-ts-mode) (python-mode . python-ts-mode) (sh-mode . bash-ts-mode)))

(setq treesit-language-source-alist '((bash . ("https://github.com/tree-sitter/tree-sitter-bash")) (c . ("https://github.com/tree-sitter/tree-sitter-c")) (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp")) (css . ("https://github.com/tree-sitter/tree-sitter-css")) (cmake . ("https://github.com/uyha/tree-sitter-cmake")) (csharp . ("https://github.com/tree-sitter/tree-sitter-c-sharp.git")) (dockerfile . ("https://github.com/camdencheek/tree-sitter-dockerfile")) (elisp . ("https://github.com/Wilfred/tree-sitter-elisp")) (go . ("https://github.com/tree-sitter/tree-sitter-go")) (gomod . ("https://github.com/camdencheek/tree-sitter-go-mod.git")) (html . ("https://github.com/tree-sitter/tree-sitter-html")) (java . ("https://github.com/tree-sitter/tree-sitter-java.git")) (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript")) (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")) (markdown . ("https://github.com/MDeiml/tree-sitter-markdown" nil "tree-sitter-markdown/src")) (ocaml . ("https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src")) (org . ("https://github.com/milisims/tree-sitter-org")) (python . ("https://github.com/tree-sitter/tree-sitter-python")) (php . ("https://github.com/tree-sitter/tree-sitter-php")) (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src")) (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "tsx/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")) (vue . ("https://github.com/merico-dev/tree-sitter-vue")) (yaml . ("https://github.com/ikatyang/tree-sitter-yaml")) (toml . ("https://github.com/tree-sitter/tree-sitter-toml")) (zig . ("https://github.com/GrayJack/tree-sitter-zig"))))

(defun my-treesit-install-langs (langs) "" (dolist (lang langs) (let* ((out-dir (locate-user-emacs-file "tree-sitter")) (soext (car dynamic-library-suffixes)) (lib-name (concat "libtree-sitter-" (symbol-name lang) soext)) (lib-path (expand-file-name lib-name out-dir))) (unless (file-exists-p lib-path) (treesit-install-language-grammar lang)))))

(unless sys-is-windows (my-treesit-install-langs '(bash c cpp cmake dockerfile elisp json python rust yaml))) #+end_src

** elisp 配置

#+begin_src emacs-lisp (use-package elisp-mode :hook (emacs-lisp-mode . my-enable-elisp-dev) :config (defun my-enable-elisp-dev () (my-enable-code-service) (treesit-parser-create 'elisp))) #+end_src

** C++ 开发配置

*** cmake-ts-mode 设置

#+begin_src emacs-lisp (use-package cmake-ts-mode :mode "\CMakeLists.txt\'" :hook (cmake-ts-mode . my-enable-code-service)) #+end_src

*** c-ts-mode 设置

#+begin_src elisp (use-package c-ts-mode :hook ((c-ts-mode c++-ts-mode) . my-enable-code-service) :custom (c-ts-mode-indent-offset 4) (c-basic-offset 4)) #+end_src

*** 导入 VS 环境变量

#+begin_src elisp (defconst my-cc--msvc-env-vars '( "DevEnvDir" "Framework40Version" "FrameworkDir" "FrameworkDIR32" "FrameworkDIR64" "FrameworkVersion" "FrameworkVersion32" "FrameworkVersion64" "INCLUDE" "LIB" "LIBPATH" "NETFXSDKDir" "PATH" "UCRTVersion" "UniversalCRTSdkDir" "user_inputversion" "VCIDEInstallDir" "VCINSTALLDIR" "VCToolsInstallDir" "VCToolsRedistDir" "VCToolsVersion" "VS170COMNTOOLS" "VisualStudioVersion" "VSINSTALLDIR" "WindowsLibPath" "WindowsSdkBinPath" "WindowsSdkDir" "WindowsSDKLibVersion" "WindowsSDKVersion" "WindowsSDK_ExecutablePath_x64" "WindowsSDK_ExecutablePath_x86" ;;/* These are special also need to be cached */ "CL" "CL" "LINK" "LINK" "TMP" "UCRTCONTEXTROOT" "VCTARGETSPATH" ) "List of environment variables required for Visual C++ to run as expected for a VS installation.")

;; 导入 vs2022 community 64位构建环境变量 (defun my-cc--import-vcvars () "Import the environment variables corresponding to a VS dev batch file." (let* ((common-dir "C:/Program Files/Microsoft Visual Studio/2022/Community/Common7/Tools") (devbat "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat") (args "amd64") (major-version "17") (bat-path (expand-file-name "cmake-tools-vcvars.bat" (temporary-file-directory))) (env-file-path (concat bat-path ".env")) (vars (make-hash-table :test 'equal))) (setq bat (format "@echo off cd /d "%%~dp0" set "VS%s0COMNTOOLS=%s" set "INCLUDE=" call "%s" %s setlocal enableextensions enabledelayedexpansion cd /d "%%~dp0" %s" major-version common-dir devbat args (mapconcat (lambda (env-var) (format "if DEFINED %s echo %s := %%%s%% >> %s" env-var env-var env-var env-file-path)) my-cc--msvc-env-vars "\n"))) (with-temp-file bat-path (insert bat)) (shell-command-to-string (concat "cmd.exe /c " bat-path))

  (if (not (file-exists-p env-file-path))
      nil
    (dolist (line (with-temp-buffer
                    (insert-file-contents env-file-path)
                    (split-string (buffer-string) "\n" t)))
      (let ((var (split-string line " := " t)))
        (puthash (string-trim (car var)) (string-trim (cadr var)) vars)))
    (if (not (gethash "INCLUDE" vars))
        nil
      (maphash #'(lambda (key value)
                   (setenv key value))
               vars)))))

#+end_src

*** cmake-integration 配置

#+begin_src elisp (use-package cmake-integration :straight (cmake-integration :type git :host github :repo "darcamo/cmake-integration")) #+end_src

** Rust 开发配置

#+begin_src elisp (use-package rust-ts-mode :mode "\.rs\'" :hook (rust-ts-mode . my-enable-code-service)) #+end_src

** python 开发配置

#+begin_src emacs-lisp (use-package python :hook (python-ts-mode . my-enable-code-service)) #+end_src

** json-ts-mode

#+begin_src elisp (use-package json-ts-mode :hook (json-ts-mode . my-enable-code-service) :custom (json-ts-mode-indent-offset 2) :config (defun my-json-generate-language-server-json () (with-temp-file (expand-file-name "vscode-json-language-server.json" my-etc-dir) (url-insert-file-contents "https://www.schemastore.org/api/json/catalog.json") (let* ((catalog (json-parse-string (buffer-substring-no-properties (point-min) (point-max)) :object-type 'plist)) (schemas (plist-get catalog :schemas)) (exclude-pattern "..\(cff\|cjs\|js\|mjs\|toml\|yaml\|yml\)$") (json-validation )) (mapc #'(lambda (schema) (when-let ((url (plist-get schema :url)) (file-match (plist-get schema :fileMatch)) (filtered-file-match (seq-filter #'(lambda (match) (and (not (string-prefix-p "!" match)) (not (string-match-p exclude-pattern match)))) file-match))) (add-to-list 'json-validation `((:url . ,url) (:fileMatch ., filtered-file-match))))) schemas)

      (let* ((ls '(:name "vscode-json-language-server" :languageId "json" :command ("vscode-json-language-server" "--stdio")))
             (json `(:schemas ,json-validation))
             (settings `(:json ,json))
             (json-encoding-pretty-print t))
        (plist-put ls :settings settings)
        (erase-buffer)
        (goto-char (point-min))
        (insert (json-encode ls))))))
)

#+end_src

** qml 开发

#+begin_src elisp (use-package qml-ts-mode :ban minimal? :straight (qml-ts-mode :type git :host github :repo "xhcoding/qml-ts-mode") :hook (qml-ts-mode . my-enable-code-service) :custom (qml-ts-mode-indent-offset 4)) #+end_src

** plantuml 支持

#+begin_src emacs-lisp (use-package plantuml-mode :ban minimal? :straight t :defer t :custom (plantuml-jar-path (expand-file-name "plantuml.jar" my-lib-dir)) (plantuml-default-exec-mode 'jar)) #+end_src

** dape 调试

#+begin_src elisp (use-package dape :straight t) #+end_src

** dash-docs 查询 dash 文档

#+begin_src elisp (use-package dash-docs :ban minimal? :straight t :commands (my/dash-docs-search) :bind (([remap apropos-documentation] . my/dash-docs-search) (" D" . my/dash-docs-search-docset)) :defer t :init ;; 初始化文档 (dolist (mode-docsets '((c++-ts-mode-hook . ("C++" "Boost" "Qt_6")) (qml-ts-mode-hook . ("Qt_6")) (cmake-ts-mode-hook . ("CMake")) (rust-ts-mode-hook . ("Rust")) (python-ts-mode-hook . ("Python_3")) )) (let ((hook (car mode-docsets)) (docsets (cdr mode-docsets))) (add-hook hook `(lambda () (setq-local dash-docs-docsets ',docsets) ))))

:custom
(dash-docs-enable-debugging nil)
(dash-docs-browser-func 'eaf-open-browser)
:config
(when sys-is-windows
  (setq dash-docs-docsets-path (expand-file-name "persist/zeal/docsets" (getenv "SCOOP"))))

;; fix can not open..
;; ref: https://github.com/dash-docs-el/dash-docs/pull/11
(defun dash-docs-sql (db-path sql)
  "Run in the db located at DB-PATH the SQL command and parse the results.

If there are errors, print them in `dash-docs-debugging-buffer'" (dash-docs-parse-sql-results (with-output-to-string (let ((error-file (when dash-docs-enable-debugging (make-temp-file "dash-docs-errors-file")))) (call-process "sqlite3" nil (list standard-output error-file) nil ;; args for sqlite3: "-list" "-init" null-device db-path sql)

       ;; display errors, stolen from emacs' `shell-command` function
       (when (and error-file (file-exists-p error-file))
         (if (< 0 (nth 7 (file-attributes error-file)))
             (with-current-buffer (dash-docs-debugging-buffer)
               (let ((pos-from-end (- (point-max) (point))))
                 (or (bobp)
                     (insert "\f\n"))
                 ;; Do no formatting while reading error file,
                 ;; because that can run a shell command, and we
                 ;; don't want that to cause an infinite recursion.
                 (format-insert-file error-file nil)
                 ;; Put point after the inserted errors.
                 (goto-char (- (point-max) pos-from-end)))
               (display-buffer (current-buffer))))
         (delete-file error-file))))))

;; 搜索文档
(defun my/dash-docs-search (&optional pattern)
  "Search doc."
  (interactive)
  (when-let ((search-pattern
              (or pattern
                  (let* ((current-symbol
                          (if (use-region-p)
                              (buffer-substring-no-properties (region-beginning) (region-end))
                            (thing-at-point 'symbol)))
                         (input-string
                          (string-trim
                           (read-string
                            (format "Pattern (%s): " current-symbol) nil))))
                    (when (string-blank-p input-string)
                      (setq input-string current-symbol))
                    input-string))))
    (dash-docs-create-buffer-connections)
    (dash-docs-create-common-connections)
    (when-let ((results (dash-docs-search search-pattern))
               (select t)
               (select-index -1)
               (select-result t))
      (setq select (completing-read "Select: "
                                    (let ((index 0))
                                      (mapcar (lambda (result)
                                                (setq index (+ index 1))
                                                (format "%s. %s" index (car result)))
                                              results))
                                    nil t))
      (setq select-index (- (string-to-number (car (split-string select "\\. "))) 1))
      (setq select-result (nth select-index results))
      (dash-docs-browse-url (cdr select-result)))))

(defun my/dash-docs-search-docset (&optional docset)
  "Search doc in `docset'"
  (interactive (list (dash-docs-read-docset
                      "Docset"
                      (dash-docs-installed-docsets))))
  (unless (boundp 'dash-docs-docsets)
    (setq-local dash-docs-docsets `(,docset)))

  (let ((old-dash-docs-docsets dash-docs-docsets))
    (unwind-protect
        (progn
        (setq-local dash-docs-docsets `(,docset))
        (call-interactively #'my/dash-docs-search)
        (setq-local dash-docs-docsets old-dash-docs-docsets))
      (setq-local dash-docs-docsets old-dash-docs-docsets))))

)

#+end_src

** shrface 让 eww 的阅读体验更好

#+begin_src elisp (use-package shrface :ban minimal? :straight t :defer t :config (shrface-basic) (shrface-trial) (shrface-default-keybindings) ; setup default keybindings (setq shrface-href-versatile t))

#+end_src

** eww 配置

#+begin_src elisp (use-package eww :ban minimal? :defer t :init (add-hook 'eww-after-render-hook #'shrface-mode) :config (require 'shrface))

#+end_src

** olivetti 居中显示内容

#+begin_src elisp (use-package olivetti :ban minimal? :straight t :defer t :hook (prog-mode text-mode outline-mode special-mode elfeed-show-mode) ) #+end_src

** org 配置 *** org-mode 美化设置

#+begin_src elisp (use-package org :ban minimal? :defer t :straight t :hook (org-mode . my--org-prettify-symbols) :custom-face ;; 设置Org mode标题以及每级标题行的大小 (org-document-title ((t (:height 1.75 :weight bold)))) (org-level-1 ((t (:height 1.2 :weight bold)))) (org-level-2 ((t (:height 1.15 :weight bold)))) (org-level-3 ((t (:height 1.1 :weight bold)))) (org-level-4 ((t (:height 1.05 :weight bold)))) (org-level-5 ((t (:height 1.0 :weight bold)))) (org-level-6 ((t (:height 1.0 :weight bold)))) (org-level-7 ((t (:height 1.0 :weight bold)))) (org-level-8 ((t (:height 1.0 :weight bold)))) (org-level-9 ((t (:height 1.0 :weight bold)))) ;; 设置代码块用上下边线包裹 (org-block-begin-line ((t (:underline t :background unspecified)))) (org-block-end-line ((t (:overline t :underline nil :background unspecified)))) :custom ;; 标题行美化 (org-fontify-whole-heading-line t) ;; 设置标题行折叠符号 (org-ellipsis " ▾") ;; TODO标签美化 (org-fontify-todo-headline t) ;; DONE标签美化 (org-fontify-done-headline t) ;; 引用块美化 (org-fontify-quote-and-verse-blocks t) ;; 隐藏宏标记 (org-hide-macro-markers t) ;; 隐藏强调标签 (org-hide-emphasis-markers t) ;; 高亮latex语法 (org-highlight-latex-and-related '(native script entities)) ;; 以UTF-8显示 (org-pretty-entities t) ;; 当启用缩进模式时自动隐藏前置星号 (org-indent-mode-turns-on-hiding-stars t) ;; 自动启用缩进 (org-startup-indented nil) ;; 根据标题栏自动缩进文本 (org-adapt-indentation nil) ;; 自动显示图片 (org-startup-with-inline-images t) ;; 默认以Overview的模式展示标题行 (org-startup-folded 'overview) ;; 允许字母列表 (org-list-allow-alphabetical t) ;; 编辑时检查是否在折叠的不可见区域 (org-fold-catch-invisible-edits 'smart) ;; 上标^下标_是否需要特殊字符包裹,这里设置需要用大括号包裹 (org-use-sub-superscripts '{}) :config

(when (my-font-installed-p "等距更纱黑体 SC")
  (create-fontset-from-fontset-spec
   (font-xlfd-name
    (font-spec :family "等距更纱黑体 SC"
               :weight 'regular
               :slant 'normal
               :registry "fontset-orgtable")))

  (set-fontset-font "fontset-orgtable" '(#x0 . #xffff)
                    (font-spec :family "等距更纱黑体 SC"
                               :weight 'regular
                               :slant 'normal))

  (set-face-attribute 'org-table nil :fontset "fontset-orgtable" :font "fontset-orgtable"))

(defun my--org-prettify-symbols ()
  (setq prettify-symbols-alist
        (mapcan (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
                '(
                  ("#+begin_src"                        . ?✎)
                  ("#+end_src"                          . ?□)
                  ("#+results:"                         . ?💻)
                  ("#+date:"                            . ?📅)
                  ("#+author:"                          . ?👤)
                  ("#+title:"                           . ?📓)
                  ("#+identifier:"                 . ?🆔)
                  ("#+hugo_tags:"                       . ?📍)
                  ("#+hugo_categories:"                 . ?📁)
                  ("#+hugo_locale:"                     . ?🌐)
                  ("#+hugo_draft:"                      . ?🚮)
                  ("#+hugo_custom_front_matter:"        . ?📝)
                  ("#+begin_quote"                      . ?«)
                  ("#+end_quote"                        . ?»)
                  )))
  (setq prettify-symbols-unprettify-at-point t)
  (prettify-symbols-mode 1))

;; 设置标题行之间总是有空格;列表之间根据情况自动加空格
(setq org-blank-before-new-entry '((heading . t)
                                   (plain-list-item . auto)
                                   )))

(use-package org-modern :ban minimal? :straight t :hook (org-mode . org-modern-mode) :config ;; 额外的行间距,0.1表示10%,1表示1px (setq-default line-spacing 0.1) ;; 复选框美化 (setq org-modern-checkbox '((?X . #("▢✓" 0 2 (composition ((2))))) (?- . #("▢–" 0 2 (composition ((2))))) (?\s . #("▢" 0 1 (composition ((1))))))) ;; 列表符号美化 (setq org-modern-list '((?- . "•") (?+ . "◦") (?* . "▹"))) ;; 代码块类型美化,我们使用了 prettify-symbols-mode' (setq org-modern-block-name nil) ;; #+关键字美化,我们使用了 prettify-symbols-mode' (setq org-modern-keyword nil) ;; 关闭表格美化 (setq org-modern-table nil) )

(use-package org-appear :ban minimal? :straight t :hook (org-mode . org-appear-mode) :config (setq org-appear-autolinks t) (setq org-appear-autosubmarkers t) (setq org-appear-autoentities t) (setq org-appear-autokeywords t) (setq org-appear-inside-latex t)) #+end_src

*** org 基本配置

#+begin_src elisp (use-package org :ban minimal? :defer t :straight t :custom (org-directory my-org-dir) (org-modules '(ol-wl)) (org-structure-template-alist '(("q" . "quote\n") ("s" . "src") ("e" . "src elisp\n") ("c" . "src cpp\n") ("h" . "export html")) )) #+end_src

*** org babel 设置

#+begin_src elisp (use-package org :ban minimal? :defer t :straight t :custom (org-confirm-babel-evaluate nil) (org-export-use-babel nil) (org-src-lang-modes '(("C" . c-ts) ("C++" . c++-ts) ("asymptote" . asy) ("bash" . sh) ("beamer" . latex) ("calc" . fundamental) ("cpp" . c++-ts) ("ditaa" . artist) ("desktop" . conf-desktop) ("dot" . fundamental) ("elisp" . emacs-lisp) ("ocaml" . tuareg) ("screen" . shell-script) ("shell" . sh) ("sqlite" . sql) ("toml" . conf-toml))) :config (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (perl . t) (python . t) (ruby . t) (js . t) (css . t) (sass . t) (C . t) (java . t) (plantuml . t) (sql . t))) ;; C 执行支持 :stdin 数据 (defun myorg-babel-execute-add-stdin(args) (let ((body (nth 0 args)) (params (nth 1 args)) (stdin (cdr (assq :stdin params))) (cmdline (cdr (assq :cmdline params))) (stdin-file (expand-file-name "input_data.txt" (temporary-file-directory))) (data)) (when stdin (setq data (save-excursion (org-babel-goto-named-src-block stdin) (org-element-property :value (org-element-at-point)))) (with-temp-file stdin-file (insert data)) (setq cmdline (concat (or cmdline "") " < " stdin-file)) (setf (alist-get :cmdline params) cmdline)) `(,body ,params) )) (advice-add #'org-babel-C-execute :filter-args 'my*org-babel-execute-add-stdin)

)

#+end_src

*** gtd 配置

#+begin_src elisp (use-package :org :ban minimal? :defer t :bind (("C-c a" . org-agenda) ("C-c c" . org-capture)) :init (setq my-org-gtd-dir (expand-file-name "Gtd" my-org-dir) my-org-inbox-file (expand-file-name "inbox.org" my-org-gtd-dir) my-org-projects-file (expand-file-name "projects.org" my-org-gtd-dir))

(defun my-org--verify-refile-target ()
  "Exclude todo keywords with a done state from refile targets."
  (not (member (nth 2 (org-heading-components)) org-done-keywords)))
 
:custom
(org-agenda-files `(,my-org-inbox-file ,my-org-projects-file))
(org-capture-templates `(("i" "Inbox" entry
                          (file ,my-org-inbox-file)
                          ,(concat "* TODO %?\n"
                                   "/Entered on/ %U"))))
(org-todo-keywords
 '((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)" "CANCELLED(c)")))

(org-refile-targets '((nil :maxlevel . 9)
                      (org-agenda-files :maxlevel . 9)))

(org-refile-use-outline-path t)
(org-outline-path-complete-in-steps nil)
(org-refile-allow-creating-parent-nodes 'confirm)

(org-refile-target-verify-function 'my-org--verify-refile-target)

(org-agenda-span 'day)
(org-agenda-hide-tags-regexp ".")
(org-agenda-prefix-format
 '((agenda . " %i %-12:c%?-12t% s")
   (todo   . " ")
   (tags   . " %i %-12:c")
   (search . " %i %-12:c")))

(org-agenda-custom-commands
 '(("g" "Get Things Done (GTD)"
    ((agenda "" ((org-deadline-warning-days 0)))
     (todo "NEXT"
           ((org-agenda-prefix-format "  %i %-12:c ")
            (org-agenda-overriding-header "\nTasks\n")))
     (tags-todo "inbox"
                ((org-agenda-prefix-format "  %?-12t% s")
                 (org-agenda-overriding-header "\nInbox\n")))))))

)

#+end_src

*** export 设置

#+begin_src emacs-lisp (use-package org :ban minimal? :straight t :defer t :hook (org-mode . my--set-org-html-head-extra) :custom (org-export-with-broken-links t) :config (defun my--set-org-html-head-extra () "Set org html head extra" (let ((path (expand-file-name "custom-head.html" my-etc-dir))) (when (file-exists-p path) (setq org-html-head-extra (with-temp-buffer (insert-file-contents path) (buffer-string)))))) )

(use-package htmlize :ban minimal? :straight t :after org)

#+end_src

*** 生成 TOC

#+begin_src emacs-lisp (use-package toc-org :ban minimal? :straight t :commands (toc-org-insert-toc) :custom (toc-org-max-depth 3)) #+end_src

*** org-contrib wanderlust 支持

#+begin_src elisp (use-package org-contrib :ban minimal? :straight t :after org) #+end_src

*** 增量载入 org 包

#+begin_src emacs-lisp (when full? (doom-load-packages-incrementally '(calendar find-func format-spec org-macs org-compat org-faces org-entities org-list org-pcomplete org-src org-footnote org-macro ob org org-agenda org-capture org-gtd))) #+end_src

** 知识管理及博客配置 *** denote

#+begin_src elisp (use-package denote :ban minimal? :straight t :custom (denote-directory my-notes-dir) :config (defun denote-blog () "Create blog for publish." (declare (interactive-only t)) (interactive) (let ((denote-directory my-blogs-org-dir) (denote-prompts '(title)) (denote-org-front-matter "#+title: %1$s ,#+date: %2$s ,#+author: xhcoding ,#+identifier: %4$s ,#+hugo_locale: zh ,#+hugo_tags: %3$s ,#+hugo_categories: %3$s ,#+hugo_draft: false

" )) (call-interactively #'denote-open-or-create)))

  (defun denote-blog-image-insert (file)
    "Insert image file to blog."
    (declare (interactive-only t))
    (interactive "fImage file: ")
    (let* ((blog-file-title (denote-retrieve-filename-title (buffer-file-name)))
           (blog-image-dir (concat (expand-file-name blog-file-title my-blogs-image-dir) "/"))
           (blog-image-ext (concat "." (file-name-extension file)))
           (blog-default-image-title (concat blog-file-title (format-time-string "-%H%M%S")))
           (blog-image-title (read-string "Image title: " "" nil blog-default-image-title))
           (blog-image-new-name (denote-format-file-name blog-image-dir (denote-retrieve-filename-identifier (buffer-file-name)) nil blog-image-title blog-image-ext nil)))
      (unless (file-directory-p blog-image-dir)
        (make-directory blog-image-dir t))
      (copy-file file blog-image-new-name)
      (insert (format "[[file:%s]]" (file-relative-name blog-image-new-name (file-name-directory (buffer-file-name)))))))
  (defun my-denote-blog-link-ol-export (link description format)
    ""
    (let* ((denote-directory my-blogs-org-dir)
           (path-id (denote-link--ol-resolve-link-to-target link :path-id))
           (path (file-relative-name (car path-id))))
      (format "[%s](%s)" description (concat my-hugo-post-url (file-name-sans-extension (downcase path))))))

  (with-eval-after-load 'org
    (org-link-set-parameters "denote" :export #'my-denote-blog-link-ol-export))


  )

#+end_src

*** hugo 配置

#+begin_src emacs-lisp (if sys-is-windows (defconst my-hugo-root-dir (expand-file-name "D:/Blog")) (if sys-is-wsl2 (defconst my-hugo-root-dir (expand-file-name "/mnt/d/Blog")) (defconst my-hugo-root-dir (expand-file-name "~/Blog")))) (defconst my-hugo-image-url "https://images.xhcoding.cn/blog")

(defconst my-hugo-post-url "https://xhcoding.cn/post/")

(use-package ox-hugo :ban minimal? :straight t :after org :custom (org-hugo-base-dir my-hugo-root-dir) (org-hugo-section "post") (org-hugo-default-section-directory "post") ;; (org-hugo-auto-set-lastmod t) :config (defun my-hugo--blogs-image-path-p (raw-path) (let ((file-path (expand-file-name raw-path))) (equal (string-match-p (regexp-quote (expand-file-name my-blogs-image-dir)) file-path) 0)))

;; D:/Data/Blogs/images/1.png ==> https://images.xhcoding.cn/blog/1.png
(defun my-hugo--image-path-to-url (raw-path)
  (let ((file-path (expand-file-name raw-path)))
    (concat my-hugo-image-url (string-trim-left file-path my-blogs-image-dir))))

;; D:/Data/Blogs/images/1.png ==> https://images.xhcoding.cn/blog/1.png
(defun my-hugo*convert-path-to-url (args)
  (let* ((link (nth 0 args))
         (desc (nth 1 args))
         (info (nth 2 args))
         (type (org-element-property :type link))
         (raw-path (org-element-property :path link)))
    (cond
     ((and (string-equal type "file") (my-hugo--blogs-image-path-p raw-path))
      (progn
        (let* ((image-url (my-hugo--image-path-to-url raw-path))
               (new-link (org-element-put-property link :path image-url )))
          `(,new-link ,desc ,info))))
     (t `(,link ,desc ,info))
     )))

(advice-add #'org-hugo-link :filter-args #'my-hugo*convert-path-to-url)

(defun my-hugo*author-to-authors(args)
  (let* ((data (nth 0 args))
         (info (nth 1 args))
         (repl-str (plist-get info :hugo-front-matter-key-replace))
         (repl-author "author>authors"))
    (message "%s" repl-str)
    (if repl-str
        (unless (string-match-p repl-author repl-str)
          (plist-put info :hugo-front-matter-key-replace (concat repl-str " " repl-author)))
      (plist-put info :hugo-front-matter-key-replace repl-author))
    `(,data ,info)))

(advice-add #'org-hugo--replace-keys-maybe :filter-args #'my-hugo*author-to-authors)

(defun my/hugo-export-all-blogs ()
  "Export all blogs."
  (interactive)
  (dolist (file (directory-files my-blogs-org-dir))
    (when (string-equal "org" (file-name-extension file))
      (with-temp-buffer
        (find-file (expand-file-name file my-blogs-org-dir))
        (org-hugo-export-to-md)))))

)

(use-package easy-hugo :ban minimal? :straight t :defer t :config (setq easy-hugo-basedir (expand-file-name my-hugo-root-dir) easy-hugo-postdir (expand-file-name my-blogs-org-dir) easy-hugo-org-header t easy-hugo-github-deploy-script "deploy.bat"))

#+end_src

*** 自动在中英文插入空格

#+begin_src elisp (use-package pangu-spacing :ban minimal? :straight t :after org :hook (org-mode . pangu-spacing-mode) :custom (pangu-spacing-real-insert-separtor t)) #+end_src

*** org-download 下载图片

#+begin_src elisp (use-package org-download :disabled t :ban minimal? :straight t :after org :config (when sys-is-windows (setq org-download-screenshot-method "irfanview /capture=4 /convert="%s"" org-download-display-inline-images 'posframe org-download-abbreviate-filename-function 'expand-file-name))

(setq-default org-download-image-dir my-blog-img-dir
              org-download-heading-lvl nil)


;; 截图的名称不要总是 screenshot
(defun my/org-download-screenshot ()
  "Capture screenshot and insert the resulting file.

The screenshot tool is determined by `org-download-screenshot-method'." (interactive) (let* ((screenshot-dir (file-name-directory org-download-screenshot-file)) (org-file-path (buffer-file-name)) (org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path))) (new-screenshot-name (concat org-file-name ".png")) (new-screenshot-path (expand-file-name new-screenshot-name screenshot-dir))) (when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path)) (setq new-screenshot-name (substring new-screenshot-name (+ 1 (string-match-p "-" new-screenshot-name))) new-screenshot-path (expand-file-name new-screenshot-name screenshot-dir))) (make-directory screenshot-dir t) (if (functionp org-download-screenshot-method) (funcall org-download-screenshot-method org-download-screenshot-file) (shell-command-to-string (format org-download-screenshot-method org-download-screenshot-file))) (when (file-exists-p org-download-screenshot-file) (rename-file org-download-screenshot-file new-screenshot-path) (org-download-image new-screenshot-path) (delete-file new-screenshot-path))))

(defun my/org-download-clipboard()
  "Download from clipboard"
  (interactive)
  (let ((org-download-screenshot-method "irfanview /clippaste /convert=\"%s\""))
    (my/org-download-screenshot)))

;; 将图片保存到当前 buffer 名称目录下
;; ref: https://github.com/abo-abo/org-download/issues/195
(defun my-org-download-method (link)
  (let* ((filename
         (file-name-nondirectory
          (car (url-path-and-query
                (url-generic-parse-url link)))))
        (org-file-path (buffer-file-name))
        (org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path)))
        (dirname (expand-file-name org-file-name my-blog-img-dir)))
    (when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path))
      (setq dirname (expand-file-name
                     (substring org-file-name (+ 1 (string-match-p "-" org-file-name)))
                     my-blog-img-dir)))
    (make-directory dirname t)
    (expand-file-name (funcall org-download-file-format-function filename) dirname)))
(setq org-download-method 'my-org-download-method))

#+end_src

** eaf 配置

#+begin_src elisp (use-package eaf :ban minimal? :disabled t :straight (emacs-application-framework :type git :host github :repo "emacs-eaf/emacs-application-framework" :files ("*") :build nil) :defer t :commands (eaf-open eaf-open-browser eaf-open-this-buffer eaf-open-pdf-from-history eaf-open-cloud-music) :init ;; 手动添加到 load-path (add-to-list 'load-path (straight--repos-dir "emacs-application-framework")) :config (setq eaf-proxy-type "http") (setq eaf-proxy-host "127.0.0.1") (setq eaf-proxy-port "7890")

  (setq eaf-webengine-default-zoom 2.5)
  (when sys-is-windows
    (setq eaf-chrome-bookmark-file (expand-file-name "~/AppData/Local/Microsoft/Edge/User Data/Default/Bookmarks"))

    (defun my-eaf--enable-python-utf8-mode (environment)
      (append (list "PYTHONUTF8=1") environment))
        (advice-add 'eaf--build-process-environment :filter-return #'my-eaf--enable-python-utf8-mode))
  (require 'eaf-browser)
  (require 'eaf-pdf-viewer)
  (require 'eaf-org-previewer)
  (require 'eaf-markdown-previewer)
  )

#+end_src

** popweb 配置

#+begin_src elisp (use-package popweb :disabled t :ban minimal? :straight (popweb :type git :host github :repo "manateelazycat/popweb" :build nil) :commands (popweb-dict-eudic-input popweb-import-browser-cookies) :bind (" t" . popweb-dict-eudic-input) :init (let ((repo (straight--repos-dir "popweb"))) (add-to-list 'load-path repo) (add-to-list 'load-path (expand-file-name "extension/dict" repo)) ) :config (require 'popweb-dict)

;; 欧陆词典,可以登录后加入生词,方便手机同步
(popweb-dict-create "eudic" "https://dict.eudic.net/dicts/en/%s"
                    (concat
                     "document.getElementsByTagName('header')[0].style.display = 'none';"
                     "document.getElementById('head-bar').style.display = 'none';"
                     "document.getElementById('head-bk').style.display = 'none';"
                     "document.getElementById('scrollToTop').style.display = 'none';"
                     "document.getElementById('bodycontent').children[4].style.display = 'none';"
                     ))
)

#+end_src

** dictionary-overlay 方便阅读英文文章

#+begin_src elisp (use-package websocket :disabled t :ban minimal? :straight t :defer t)

(use-package websocket-bridge :disabled t :ban minimal? :straight (websocket-bridge :type git :host github :repo "ginqi7/websocket-bridge") :defer t)

(use-package dictionary-overlay :disabled t :ban minimal? :straight (dictionary-overlay :type git :host github :repo "ginqi7/dictionary-overlay" :build nil) :defer t :init (add-to-list 'load-path (straight--repos-dir "dictionary-overlay")) :custom (dictionary-overlay-python "python")) #+end_src

** 邮件配置

*** Wanderlust 邮件前端

Wanderlust + offlineimap3 + mu

#+begin_src emacs-lisp (use-package wl :disabled t :ban minimal? :straight (wanderlust) :defer t :hook ((wl . meow-motion-mode)) :bind (:map wl-folder-mode-map (("q" . wl-folder-suspend))) :init (setq wl-init-file (expand-file-name "wl.el" my-private-dir) wl-folders-file (expand-file-name "folders.wl" my-private-dir) wl-address-file (expand-file-name "address.wl" my-private-dir)) :config (if (boundp 'mail-user-agent) (setq mail-user-agent 'wl-user-agent)) (if (fboundp 'define-mail-user-agent) (define-mail-user-agent 'wl-user-agent 'wl-user-agent-compose 'wl-draft-send 'wl-draft-kill 'mail-send-hook)) (setq wl-quicksearch-folder "[]")

(setq wl-message-ignored-field-list
    '(".")
    wl-message-visible-field-list
    '("^\\(To\\|Cc\\):"
      "^Subject:"
      "^\\(From\\|Reply-To\\):"
      "^\\(Posted\\|Date\\):"
      "^Organization:"
      "^X-\\(Face\\(-[0-9]+\\)?\\|Weather\\|Fortune\\|Now-Playing\\):")
    wl-message-sort-field-list
    (append wl-message-sort-field-list
            '("^Reply-To" "^Posted" "^Date" "^Organization")))

;; windows 上 mu find 返回的路径以 /cygdrive/ 开头,我们需要自己处理一下
(defun my--elmo-search-parse-filename-list ()
  (let (bol locations)
    (goto-char (point-min))
    (while (not (eobp))
      (beginning-of-line)
      (when (and elmo-search-use-drive-letter
                 (looking-at "^\\(/cygdrive/\\)?\\([A-Za-z]\\)\\([:|]\\)?/"))
        (replace-match "/\\2:/")
        (beginning-of-line))
      (unless (looking-at "^file://")
        (insert "file://")
        (beginning-of-line))
      (setq bol (point))
      (end-of-line)
      (setq locations (cons (buffer-substring bol (point)) locations))
      (forward-line))
    (nreverse locations)))


(elmo-search-register-engine
 'mu-msys 'local-file
 :prog "mu"
 :args '("find" elmo-search-split-pattern-list "--fields" "l")
 :charset 'utf-8
 :parser 'my--elmo-search-parse-filename-list)

(setq elmo-search-default-engine 'mu-msys)

;; mu 的输入要用 gbk 编码,不然无法输入中文
(add-to-list 'process-coding-system-alist '("mu" utf-8 . gbk))

;; mime 附件保存目录
(setq mime-save-directory (expand-file-name "mails" my-archives-dir))
)

#+end_src

调用 compose-mail 前先 require wanderlust

#+begin_src emacs-lisp (when full?

(defun my-*require-wanderlust (&rest _)
  (require 'wl))

(advice-add 'compose-mail :before #'my-*require-wanderlust))

#+end_src

*** alert-toast 邮件通知

#+begin_src emacs-lisp (use-package alert-toast :disabled t :ban minimal? :straight t :after wl :config (defun my--notify-new-mail-arrived (number) (alert-toast-notify `(:title "Wanderlust" :message ,(format "你有 %s 封未读邮件" number))))

(add-hook 'wl-biff-new-mail-hook #'my--notify-new-mail-arrived)

)

#+end_src

** elfeed RSS 订阅

#+begin_src emacs-lisp (use-package elfeed :ban minimal? :straight t :defer t :config (defun my-eaf-elfeed-open-url () "Display the currently selected item in an eaf buffer." (interactive) (let ((entry (elfeed-search-selected :ignore-region))) (require 'elfeed-show) (when (elfeed-entry-p entry) ;; Move to next feed item. (elfeed-untag entry 'unread) (elfeed-search-update-entry entry) (unless elfeed-search-remain-on-entry (forward-line)) (my/toggle-one-window) (eaf-open-browser (elfeed-entry-link entry)) ))) )

(use-package elfeed-org :ban minimal? :straight t :after elfeed :config (elfeed-org) (setq rmh-elfeed-org-files `(,(expand-file-name "elfeed.org" my-org-dir)))) #+end_src

** leetcode.el 刷题

#+begin_src elisp (use-package leetcode :ban minimal? :straight (leetcode :type git :host github :repo "xhcoding/leetcode.el") :defer t :custom (leetcode-prefer-language "cpp") (leetcode-save-solutions t) (leetcode-directory (expand-file-name "Project/LeetCode" my-code-dir)) :config ;; 在 leetcode 里关闭自动补全 (defun my-after-leetcode--start-coding (problem problem-info) (let-alist problem (let ((title (leetcode-problem-title problem-info)) (code-buf-name (leetcode--get-code-buffer-name title))) (with-current-buffer (leetcode--get-code-buffer code-buf-name) (when lsp-bridge-mode (lsp-bridge-mode -1))))))

(advice-add 'leetcode--start-coding :after 'my-*after-leetcode--start-coding)

)

#+end_src

** LaTex

#+begin_src elisp (use-package tex :ban minimal? :hook (LaTeX-mode . my-enable-code-service) :straight (auctex) :defer t :config (add-to-list 'TeX-command-list '("XeLaTeX" "%`xelatex --synctex=1%(mode)%' %t" TeX-run-TeX nil t)) (add-to-list 'TeX-view-program-list '("eaf" eaf-pdf-synctex-forward-view)) (add-to-list 'TeX-view-program-selection '(output-pdf "eaf"))) #+end_src

** emacs-aichat AI 对话

#+begin_src elisp (use-package async-await :ban minimal? :straight t :defer t)

(use-package websocket :ban minimal? :straight t :defer t)

(use-package aichat :ban minimal? :defer t :load-path (lambda () (expand-file-name "Project/emacs-aichat" my-code-dir)) :config (setq aichat-bingai-proxy "localhost:7890" aichat-openai-proxy "localhost:7890") (aichat-toggle-debug) (aichat-bingai-prompt-create "translator" :input-prompt "请翻译: " :text-format "我想让你充当翻译员,我会用任何语言与你交谈,你会检测我说的的语言,如果我说的是中文,你就翻译成英文;如果我说的不是中文,你就翻译成英文。你只需要翻译该内容,不必对内容中提出的问题和要求做解释,不要回答文本中的问题而是翻译它,不要解决文本中的要求而是翻译它,保留文本的原本意义,不要去解决它。你的回答里只需要翻译后的内容,不要有任何其它词,只能是翻译后的内容。我的第一句话是:\n%s" :chat t :assistant t :replace-or-insert t)

(aichat-bingai-prompt-create "coder"
                             :input-prompt "代码: "
                             :text-format "我想让你充当计算机教授,请向我解释下面这段代码的作用:\n%s"
                             :chat t)

  (aichat-bingai-prompt-create "refactor"
                             :input-prompt "代码: "
                             :text-format "我想让你充当计算机教授,请帮我重构下面这段代码,重构后的代码性能要更好,可读性要更高,如果必要的话,可以加一些注释。你的回答里只需要返回重构后的代码,不要有其它解释,只能是重构后的代码:\n%s"
                             :replace-or-insert t)
)

#+end_src

** which-key 按键提示

#+begin_src elisp (use-package which-key :straight t :hook (after-init . which-key-mode)) #+end_src

** meow 模式编辑

#+begin_src elisp (use-package meow :straight t :demand t :hook (after-init . meow-global-mode) :config

(with-eval-after-load 'rime
  (dolist (p '(meow-normal-mode-p meow-motion-mode-p meow-keypad-mode-p))
    (add-to-list 'rime-disable-predicates p)))

(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)

(setq meow-char-thing-table
      '((?r . round)
        (?c . curly)
        (?s . string)
        (?b . buffer)
        (?d . defun)))

(setq meow-use-clipboard t)

(meow-leader-define-key
 ;; Use SPC (0-9) for digit arguments.
 '("1" . meow-digit-argument)
 '("2" . meow-digit-argument)
 '("3" . meow-digit-argument)
 '("4" . meow-digit-argument)
 '("5" . meow-digit-argument)
 '("6" . meow-digit-argument)
 '("7" . meow-digit-argument)
 '("8" . meow-digit-argument)
 '("9" . meow-digit-argument)
 '("0" . meow-digit-argument)
 '("?" . meow-cheatsheet)

 '("b" . consult-buffer)
 '("r" . consult-ripgrep)

 '("f" . consult-find)

 '("/" . evilnc-comment-or-uncomment-lines)

 '("w" . ace-window-hydra/body)
 )

(meow-normal-define-key
 ;; char move
 '("j" . meow-next)
 '("J" . meow-next-expand)
 '("k" . meow-prev)
 '("K" . meow-prev-expand)
 '("l" . meow-right)
 '("L" . meow-right-expand)
 '("h" . meow-left)
 '("H" . meow-left-expand)

 ;; word move
 '("e" . meow-next-word)
 '("E" . meow-next-symbol)
 '("b" . meow-back-word)
 '("B" . meow-back-symbol)

 ;; line move
 '("x" . meow-line)
 '("X" . meow-goto-line)

 ;; thing move
 '("," . meow-inner-of-thing)
 '("." . meow-bounds-of-thing)
 '("[" . meow-beginning-of-thing)
 '("]" . meow-end-of-thing)
 '("o" . meow-block)
 '("O" . meow-to-block)

 ;; jump
 '("n" . meow-search)
 '("f" . meow-find)
 '("v" . meow-visit)

 ;; action
 '("i" . meow-insert)
 '("I" . meow-open-above)
 '("a" . meow-append)
 '("A" . meow-open-below)
 '("y" . meow-save)
 '("Y" . meow-sync-grab)
 '("c" . meow-change)
 '("r" . meow-replace)
 '("R" . meow-swap-grab)
 '("p" . meow-yank)
 '("s" . meow-kill)
 '("d" . meow-delete)
 '("D" . meow-backward-delete)
 '("G" . meow-grab)
 '("m" . meow-join)
 '("t" . meow-till)
 '("u" . undo)
 '("U" . meow-undo)
 '("w" . meow-mark-word)
 '("W" . meow-mark-symbol)
 '("z" . meow-pop-selection)

 ;; others
 '("0" . meow-expand-0)
 '("9" . meow-expand-9)
 '("8" . meow-expand-8)
 '("7" . meow-expand-7)
 '("6" . meow-expand-6)
 '("5" . meow-expand-5)
 '("4" . meow-expand-4)
 '("3" . meow-expand-3)
 '("2" . meow-expand-2)
 '("1" . meow-expand-1)
 '("-" . negative-argument)
 '(";" . meow-reverse)
 '("g" . meow-cancel-selection)
 '("q" . meow-quit)
 '("'" . repeat)
 '("<escape>" . ignore)
 )
)

#+end_src

** 启动 emacs server

#+begin_src elisp (when sys-is-windows (unless (daemonp) (server-start))) #+end_src

** 载入私有配置文件

#+begin_src elisp (ignore-errors (load (expand-file-name "config.el" my-private-dir)))

#+end_src