Emacs 配置

1. 前言

这是我自己的 emacs 配置!

2. early-init.el

emacs 刚启动时执行的配置文件,此时还未加载主配置文件

;;; early-init.el --- Emacs pre-initialization config -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

;; 设置垃圾回收参数
(setq gc-cons-threshold most-positive-fixnum)
(setq gc-cons-percentage 0.6)

;; 不要在 gc 期间压缩字体
(setq inhibit-compacting-font-caches t)

;; 显示真实的路径,而不是符号链接
(setq find-file-visit-truename t)

;; 禁止启动的首页展示
(setq package-enable-at-startup nil)

;; 禁止自动缩放窗口先
(setq frame-inhibit-implied-resize t)

;; 禁止菜单栏、工具栏、滚动条模式,禁止启动屏幕和文件对话框
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(setq use-file-dialog nil)

;; 此阶段不编译
(setq comp-deferred-compilation nil)

; 不显示编译警告
(setq native-comp-async-report-warnings-errors nil)

(cd "~")

(setenv "LSP_USE_PLISTS" "true")

;; Increase the amount of data which Emacs reads from the process
(setq read-process-output-max (* 1024 1024))

; custom.el
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

(provide 'early-init)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; early-init.el ends here

3. init.el

init.el 是 emacs 的主配置文件

3.1. init.el 文件头

;;; init.el --- The main init entry for Emacs -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

3.2. 使用 elpaca 作为包管理器

elpaca 是一款更加现代化的 GUI Emacs 包管理器,它拥有着相较于 straight.el

以下源自官方的安装指南:

(defvar elpaca-installer-version 0.7)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order
  '(elpaca
    :repo "https://github.com/progfolio/elpaca.git"
    :ref nil
    :depth 1
    :files (:defaults "elpaca-test.el" (:exclude "extensions"))
    :build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
   (build (expand-file-name "elpaca/" elpaca-builds-directory))
   (order (cdr elpaca-order))
   (default-directory repo))
  (add-to-list
   'load-path
   (if (file-exists-p build)
   build
     repo))
  (unless (file-exists-p repo)
    (make-directory repo t)
    (when (< emacs-major-version 28)
  (require 'subr-x))
    (condition-case-unless-debug err
    (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
       ((zerop
         (apply #'call-process
            `("git" nil ,buffer t "clone" ,@
              (when-let ((depth (plist-get order :depth)))
            (list
             (format "--depth=%d" depth)
             "--no-single-branch"))
              ,(plist-get order :repo) ,repo))))
       ((zerop
         (call-process "git"
               nil
               buffer
               t
               "checkout"
               (or (plist-get order :ref) "--"))))
       (emacs (concat invocation-directory invocation-name))
       ((zerop
         (call-process emacs
               nil
               buffer
               nil
               "-Q"
               "-L"
               "."
               "--batch"
               "--eval"
               "(byte-recompile-directory \".\" 0 'force)")))
       ((require 'elpaca))
       ((elpaca-generate-autoloads "elpaca" repo)))
      (progn
        (message "%s" (buffer-string))
        (kill-buffer buffer))
      (error
       "%s"
       (with-current-buffer buffer
         (buffer-string))))
  ((error) (warn "%s" err) (delete-directory repo 'recursive))))
  (unless (require 'elpaca-autoloads nil t)
    (require 'elpaca)
    (elpaca-generate-autoloads "elpaca" repo)
    (load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
(when (eq system-type 'windows-nt)
  ; 当 windows 平台时,关闭软链接,同时限制 elpaca 的并发数目
  (setq elpaca-queue-limit 10)
  (elpaca-no-symlink-mode))

3.3.elpaca 安装 use-package

设置 use-package 自动安装包,然后启用 elpaca-use-package-mode

(elpaca
    elpaca-use-package
  ;; Enable use-package :ensure support for Elpaca.
  (elpaca-use-package-mode))

使得 use-package 默认自动安装所有包

(setq use-package-always-ensure t)

3.4. 定义几个变量

(defvar cabins--os-win (memq system-type '(ms-dos windows-nt cygwin)))
(defvar cabins--os-mac (eq system-type 'darwin))

(when (and cabins--os-win
           (boundp 'w32-get-true-file-attributes))
  (setq w32-get-true-file-attributes nil
        w32-pipe-read-delay 0
        w32-pipe-buffer-size (* 64 1024)))
   
(setq text-quoting-style 'straight)

3.5. 加载其他的模块

;; 将lisp目录放到加载路径的前面以加快启动速度
(let ((dir (locate-user-emacs-file "lisp")))
  (add-to-list 'load-path (file-name-as-directory dir)))

;; 加载各模块化配置
;; 不要在`*message*'缓冲区显示加载模块化配置的信息
(with-temp-message ""
  (require 'init-ui) ; UI交互
  (require 'init-edit) ; 编辑行为
  (require 'init-md) ; markdown 支持
  (require 'init-org) ; org 相关设置
  (require 'init-completion) ; 补全系统
  (require 'init-dev) ; 版本管理
  (require 'init-tools) ; tools
  (require 'init-lsp) ; lsp 支持
  (require 'init-treesitter) ; treesitter 支持
  (require 'init-lang) ; lang 支持
  (require 'init-blog) ; blog 支持
  (require 'init-ai) ; ai 支持
  )

3.6. init.el 文件尾

(provide 'init)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init.el ends here

4. init-ui.el

4.1. init-ui.el 文件头

;;; init-ui.el --- UI settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

4.2. 字体设置

emacs 字体与字符集,该文章介绍了 emacs 中的字体和字符集的设置。

字体配置,采取定义字符串列表的方式,会优先使用查找到的字体

(setq my-default-fonts '("Maple Mono SC NF"))
(setq my-emoji-fonts '("Noto Color Emoji"
                       "Segoe UI Emoji"
                       "Symbola"))
(setq my-bmp-fonts '("Segoe UI Symbol"
                     "Symbola" "Symbol"))
(setq my-cjk-fonts '("霞鹜文楷等宽"
                     "微软雅黑 CN"
                     "Microsoft Yahei UI"
                     "Microsoft Ya"))

定义一个函数用于查找字体是否存在

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

设置全局默认通用的字体

(when (display-graphic-p)
  (cl-loop for font in my-default-fonts
           when (font-installed-p font)
           return (set-face-attribute 'default nil :font
                                      (font-spec :family font :weight 'normal :slant 'normal :size
                                                 (cond ((eq system-type 'gnu/linux) 14.0)
                                                       ((eq system-type 'windows-nt) 12.5))))))

设置 emoji 字体

(when (display-graphic-p)
  (cl-loop for font in my-emoji-fonts
           when (font-installed-p font)
           return (set-fontset-font t 'emoji
                                    (font-spec :family font
                                               :size (cond ((eq system-type 'gnu/linux) 16.5)
                                                           ((eq system-type 'windows-nt) 15.0)))
                                    nil 'prepend)))

设置 unicode-bmp 字体

(when (display-graphic-p)
     (cl-loop for font in my-bmp-fonts
             when (font-installed-p font)
             return (set-fontset-font t 'unicode-bmp
                                      (font-spec :family font
                                                 :size (cond ((eq system-type 'gnu/linux) 16.5)
                                                             ((eq system-type 'windows-nt) 15.0)))
                                      nil 'prepend)))

设置中文字体

(when (display-graphic-p)
  (cl-loop for font in my-cjk-fonts
           when (font-installed-p font)
           return (set-fontset-font t 'han
                                    (font-spec :name font
                                               :weight 'normal
                                               :slant 'normal
                                               :size (cond ((eq system-type 'gnu/linux) 16.5)
                                                           ((eq system-type 'windows-nt) 15.0))))))

4.3. ef theme

ef themes 是我非常喜欢的一个主题包。

(use-package modus-themes)

4.4. auto-dark

自动调节为黑暗模式

(use-package auto-dark
  :custom
  (auto-dark-dark-theme 'modus-vivendi "auto dark theme")
  (auto-dark-light-theme 'modus-operandi "auto light theme")
  :config
  (auto-dark-mode t))

4.5. 其他UI零散设置项

(when (display-graphic-p)
  (pixel-scroll-precision-mode 1))
;; 禁用一些GUI特性
(setq use-dialog-box nil) ; 鼠标操作不使用对话框
(setq inhibit-default-init t) ; 不加载 `default' 库
(setq inhibit-startup-screen t) ; 不加载启动画面
(setq inhibit-startup-message t) ; 不加载启动消息
(setq inhibit-startup-buffer-menu t) ; 不显示缓冲区列表

;; 草稿缓冲区默认文字设置
(setq initial-scratch-message
      (concat
       ";; Happy hacking, " (capitalize user-login-name) " - Emacs ♥ you!\n\n"))
       
;; 设置缓冲区的文字方向为从左到右
(setq bidi-paragraph-direction 'left-to-right)
;; 禁止使用双向括号算法
(setq bidi-inhibit-bpa t)

;; 设置自动折行宽度为80个字符,默认值为70
(setq-default fill-column 80)

;; 设置大文件阈值为100MB,默认10MB
(setq large-file-warning-threshold 100000000)

;; 以16进制显示字节数
(setq display-raw-bytes-as-hex t)
;; 有输入时禁止 `fontification' 相关的函数钩子,能让滚动更顺滑
(setq redisplay-skip-fontification-on-input t)

;; 禁止响铃
(setq ring-bell-function 'ignore)

;; 禁止闪烁光标
(blink-cursor-mode -1)

;; 在光标处而非鼠标所在位置粘贴
(setq mouse-yank-at-point t)

;; 拷贝粘贴设置
(setq select-enable-primary nil) ; 选择文字时不拷贝
(setq select-enable-clipboard t) ; 拷贝时使用剪贴板

;; 鼠标滚动设置
                                        ;(setq scroll-step 2)
                                        ;(setq scroll-margin 2)
                                        ;(setq hscroll-step 2)
                                        ;(setq hscroll-margin 2)
                                        ;(setq scroll-conservatively 101)
                                        ;(setq scroll-up-aggressively 0.01)
                                        ;(setq scroll-down-aggressively 0.01)
                                        ;(setq scroll-preserve-screen-position 'always)
                                       
;; 对于高的行禁止自动垂直滚动
(setq auto-window-vscroll nil)

;; 设置新分屏打开的位置的阈值
(setq split-width-threshold (assoc-default 'width default-frame-alist))
(setq split-height-threshold nil)

;; TAB键设置,在Emacs里不使用TAB键,所有的TAB默认为4个空格
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

;; yes或no提示设置,通过下面这个函数设置当缓冲区名字匹配到预设的字符串时自动回答yes
(setq original-y-or-n-p 'y-or-n-p)
(defalias 'original-y-or-n-p (symbol-function 'y-or-n-p))
(defun default-yes-sometimes (prompt)
  "automatically say y when buffer name match following string"
  (if (or (string-match "has a running process" prompt)
          (string-match "does not exist; create" prompt)
          (string-match "modified; kill anyway" prompt)
          (string-match "Delete buffer using" prompt)
          (string-match "Kill buffer of" prompt)
          (string-match "still connected.  Kill it?" prompt)
          (string-match "Shutdown the client's kernel" prompt)
          (string-match "kill them and exit anyway" prompt)
          (string-match "Revert buffer from file" prompt)
          (string-match "Kill Dired buffer of" prompt)
          (string-match "delete buffer using" prompt)
          (string-match "Kill all pass entry" prompt)
          (string-match "for all cursors" prompt)
          (string-match "Do you want edit the entry" prompt))
      t
    (original-y-or-n-p prompt)))
(defalias 'yes-or-no-p 'default-yes-sometimes)
(defalias 'y-or-n-p 'default-yes-sometimes)

;; 设置剪贴板历史长度300,默认为60
(setq kill-ring-max 300)

;; 在剪贴板里不存储重复内容
(setq kill-do-not-save-duplicates t)

;; 设置位置记录长度为6,默认为16
;; 可以使用 `counsel-mark-ring' or `consult-mark' (C-x j) 来访问光标位置记录
;; 使用 C-x C-SPC 执行 `pop-global-mark' 直接跳转到上一个全局位置处
;; 使用 C-u C-SPC 跳转到本地位置处
(setq mark-ring-max 6)
(setq global-mark-ring-max 6)

;; 设置 emacs-lisp 的限制
(setq max-lisp-eval-depth 10000) ; 默认值为 800
(setq max-specpdl-size 10000) ; 默认值为 1600

;; 启用 `list-timers', `list-threads' 这两个命令
(put 'list-timers 'disabled nil)
(put 'list-threads 'disabled nil)

;; 在命令行里支持鼠标
(xterm-mouse-mode 1)

;; 在模式栏上显示当前光标的列号
(column-number-mode t)

4.6. 编码设置

统一使用 UTF-8 编码。

(if (eq system-type 'windows-nt)
    (progn
      ;;use unicode everywhere
      (when (fboundp 'set-charset-priority)
        (set-charset-priority 'unicode))
      (prefer-coding-system 'utf-8-unix)
      (modify-coding-system-alist 'process "*" 'utf-8-unix)
      (set-buffer-file-coding-system 'utf-8)
      (set-file-name-coding-system 'utf-8-unix)
      (set-default-coding-systems 'utf-8-unix)
      (set-keyboard-coding-system 'utf-8-unix)
      (set-terminal-coding-system 'utf-8-unix)
      (set-language-environment "UTF-8")
      (setq locale-coding-system 'utf-8-unix)
      (setq default-process-coding-system '(utf-8-unix . utf-8-unix))
     
      ;;windows没有启用unicode时,中文语言是gbk编码gb18030会导致有些中文字符找不到字体
      ;; 系统如果开启了 unicode 支持,那么就不用设置这个
                                        ;(when (eq system-type 'windows-nt)
                                        ;  (setq locale-coding-system 'chinese-gbk))
     
      ;;The clipboard on windows dose not play well with utf8
      (unless (eq system-type 'windows-nt)
        (set-clipboard-coding-system 'utf-8)
        (set-selection-coding-system 'utf-8))
       
      ;; 英文日期,会影响日期格式
      (setq system-time-locale "C")))
(if (eq system-type 'gnu/linux)
    (progn
      (setq locale-coding-system 'utf-8)
      (set-terminal-coding-system 'utf-8)
      (set-keyboard-coding-system 'utf-8)
      (set-selection-coding-system 'utf-8)
      (set-default-coding-systems 'utf-8)
      (set-language-environment 'utf-8)
      (set-clipboard-coding-system 'utf-8)
      (set-file-name-coding-system 'utf-8)
      (set-buffer-file-coding-system 'utf-8)
      (prefer-coding-system 'utf-8)
      (modify-coding-system-alist 'process "*" 'utf-8)
      (when (display-graphic-p)
        (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))
      )
    )

4.7. doom-modeline插件

doom-modeline 是一个模式栏美化插件。

(use-package
 doom-modeline
 :config (doom-modeline-mode)
 :custom
 (doom-modeline-hub t)
 (doom-modeline-buffer-file-name-style 'file-name)
 (doom-modeline-total-line-number t)
 (doom-modeline-irc nil)
 (doom-modeline-mu4e nil)
 (doom-modeline-gnus nil)
 (doom-modeline-github nil)
 (doom-modeline-enable-word-count t))

4.8. nerd-font

(use-package nerd-icons
  :custom
  (nerd-icons-font-family "Maple Mono SC NF")
  :defer t)

4.9. diredfl

(use-package diredfl
  :hook (dired-mode . diredfl-mode))

4.10. dired 配置

有意思的是,这个是给 ls 传递参数,在 windows 下居然也可以正常工作。

(setq dired-listing-switches "-alh --group-directories-first")

4.11. treemacs

更牛逼的文件管理器

(use-package treemacs
  :defer t
  :init
  (with-eval-after-load 'winum
    (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
  :custom
  (treemacs-default-visit-action 'treemacs-visit-node-close-treemacs "close treemacs after opening file")
  :bind
  (:map global-map
        ("M-0"       . treemacs-select-window)
        ("C-x t 1"   . treemacs-delete-other-windows)
        ("C-x t t"   . treemacs)
        ("C-x t d"   . treemacs-select-directory)
        ("C-x t B"   . treemacs-bookmark)
        ("C-x t C-t" . treemacs-find-file)
        ("C-x t M-t" . treemacs-find-tag)))

4.11.1. treemacs-icons-dired

(use-package treemacs-icons-dired
  :hook (dired-mode . treemacs-icons-dired-enable-once)
  :ensure t)

4.11.2. treemacs-projectile

(use-package treemacs-projectile
  :after (treemacs projectile))

4.12. init-ui.el 文件尾

(provide 'init-ui)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-ui.el ends here

5. init-edit.el

5.1. init-edit.el 文件头

;;; init-edit.el --- Editing settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

5.2. 行号设置

在 prog-mode 下开启行号,但是在 org-mode 下不开启行号

(add-hook 'prog-mode-hook
          (lambda ()
            (unless (eq major-mode 'org-mode)
              (display-line-numbers-mode 1))))

5.3. Emacs备份设置

不使用Emacs的自动备份设置。

(setq make-backup-files nil) ; 不自动备份
(setq auto-save-default nil) ; 不使用Emacs自带的自动保存

5.4. 解除一些不常用的快捷键

将一些不常用的快捷键解除,防止误操作。

;; 解除不常用的快捷键定义
(global-set-key (kbd "C-z") nil)
(global-set-key (kbd "s-q") nil)
(global-set-key (kbd "M-z") nil)
(global-set-key (kbd "M-m") nil)
(global-set-key (kbd "C-x C-z") nil)
(global-set-key [mouse-2] nil)

5.5. 自动重载设置

当我们的文件发生了改变后,我们希望Emacs里打开的永远是最新的文件,这个时候,我们需要对自动重载进行设置,让我们的Emacs在文件发生改变的时候自动重载文件。

(use-package
  autorevert
  :defer t
  :ensure nil
  :config (global-auto-revert-mode)
  :custom
  (auto-revert-interval 10)
  (auto-revert-avoid-polling t)
  (auto-revert-verbose nil)
  (auto-revert-remote-files t)
  (auto-revert-check-vc-info t)
  (global-auto-revert-non-file-buffers t))

5.6. indent bar

使用 indent-bars 尝试进行快速的缩进

(use-package indent-bars
  :ensure (indent-bars :type git :host github :repo "jdtsmith/indent-bars")
  :hook (prog-mode . indent-bars-mode))

5.7. symbol-overlay

更高效地符号高亮插件

(use-package symbol-overlay :defer t :hook (prog-mode . symbol-overlay-mode))

5.8. flycheck

flycheck,属于是一个广泛使用的语法检查包!

(use-package flycheck :defer t :init (global-flycheck-mode))

5.9. 彩虹括号匹配

rainbow-delimiters 插件将多彩显示括号等分隔符。

(use-package
 rainbow-delimiters
 :hook (prog-mode . rainbow-delimiters-mode))

5.10. savehist 保存 minibuffer 历史记录

(use-package savehist :ensure nil :init (savehist-mode) :defer t)

5.11. save-place 记住关闭文件是光标位置

;; 自动记住每个文件的最后一次访问的光标位置
(use-package saveplace :ensure nil :init (save-place-mode) :defer t)

5.12. recentf 记住最近打开文件的历史记录

(use-package
  recentf
  :defer t
  :ensure nil
  :init (recentf-mode)
  :custom
  (recentf-max-saved-items 300)
  (recentf-exclude
   '("treemacs-persist"
     ;; 排除 ~/.emacs.d/ 目录下除了 readme.org 和 config.org 之外的所有文件
     (lambda (file)
       (and (string-match-p "^~/.emacs.d/" file)
            (not (string-match-p "\\(?:[Rr][Ee][Aa][Dd][Mm][Ee]\\.org\\|config\\.org\\)$" file))))
     ".org-registry.el"
     )))

5.13. olivetti 次要模式自动平衡窗口边距

olivetti,该包将会自动调整窗口的边距

(use-package olivetti
  :hook ((org-mode . olivetti-mode)
         (markdown-mode . olivetti-mode))
  :custom (olivetti-body-width 0.75))

5.14. format all code

使用 emacs-format-all-the-code 来进行格式化操作,不使用 lsp 的 format 功能

(use-package format-all
  :commands format-all-mode
  :hook (prog-mode . format-all-mode))

5.15. rime

emacs-rime,emacs rime 模块,让 emacs 本身支持使用 rime。

(use-package rime
  :demand t
  :custom
  (default-input-method "rime")
  :bind
  (:map rime-mode-map
        ("C-`" . 'rime-send-keybinding)))

5.16. init-edit.el 文件尾

;; (message "init-base configuration: %.2fs"
;;          (float-time (time-subtract (current-time) my/init-base-start-time)))

(provide 'init-edit)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-edit.el ends here

6. init-org.el

6.1. init-org.el 文件头

;;; init-org.el --- Org mode settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

6.2. org 调整

org 还是使用最新的比较好!!!

(use-package org
  :ensure t
  :custom-face
  ;; 设置Org mode标题以及每级标题行的大小
  (org-document-title ((t (:height 1.3 :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))))
  ;; 处理掉超链接默认的高亮
  (org-link ((t (:foreground unspecified :underline t))))
  :custom
  ;; 自动开启 indent mode
  (org-startup-indented t)
  ;; 允许字母列表
  (org-list-allow-alphabetical t)
  ;; 编辑时检查是否在折叠的不可见区域
  (org-fold-catch-invisible-edits 'smart)
  ;; 设置图片的最大宽度,如果有imagemagick支持将会改变图片实际宽度
  ;; 四种设置方法:(1080), 1080, t, nil
  (org-image-actual-width nil)
  ;; 处理中文的换行问题
  (word-wrap-by-category t)
  ;; 设置标题行之间总是有空格;列表之间根据情况自动加空格
  (org-blank-before-new-entry
   '((heading . t)
     (plain-list-item . auto)))
  ;; 设置Org mode的目录
  (org-directory "~/org")
  ;; 设置笔记的默认存储位置
  (org-default-notes-file (expand-file-name "capture.org" org-directory))
  ;; 启用一些子模块
  (org-modules '(ol-bibtex ol-gnus ol-info ol-eww org-habit org-protocol))
  ;; 标题行美化
  (org-fontify-whole-heading-line t)
  ;; 设置标题行折叠符号
  (org-ellipsis " ▾")
  ;; 在活动区域内的所有标题栏执行某些命令
  (org-loop-over-headlines-in-active-region t)
  ;; 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-with-inline-images t)
  ;; 默认以Overview的模式展示标题行
  ;; (org-startup-folded 'content)
  ;; 允许字母列表
  (org-list-allow-alphabetical t)
  ;; 列表的下一级设置
  (org-list-demote-modify-bullet
   '(("-"  . "+")
     ("+"  . "1.")
     ("1." . "a.")))
  ;; 编辑时检查是否在折叠的不可见区域
  (org-fold-catch-invisible-edits 'smart)
  ;; 在当前位置插入新标题行还是在当前标题行后插入,这里设置为当前位置
  (org-insert-heading-respect-content nil)
  ;; imenu的最大深度,默认为2
  (org-imenu-depth 4)
  ;; 回车要不要触发链接,这里设置不触发
  (org-return-follows-link nil)
  ;; 上标^下标_是否需要特殊字符包裹,这里设置需要用大括号包裹
  (org-use-sub-superscripts '{})
  ;; 复制粘贴标题行的时候删除id
  (org-clone-delete-id t)
  ;; 粘贴时调整标题行的级别
  (org-yank-adjusted-subtrees t)
  ;; 使用专家模式选择标题栏状态
  (org-use-fast-todo-selection 'expert)
  ;; 父子标题栏状态有依赖
  (org-enforce-todo-dependencies t)
  ;; 标题栏和任务复选框有依赖
  (org-enforce-todo-checkbox-dependencies t)
  ;; 优先级样式设置
  (org-priority-faces '((?A :foreground "red")
                        (?B :foreground "orange")
                        (?C :foreground "yellow")))
  :config
  (if (eq system-type 'windows-nt)
      (plist-put org-format-latex-options :scale 1.25)
    (plist-put org-format-latex-options :scale 2.5))
  )

6.3. org-modern

org-modern 美化 org-mode 的插件。

(use-package org-modern
  :after org
  :custom
  (org-modern-hide-stars 'leading)
  (line-spacing 0.1)
  ;; 由于字体问题,禁用掉 table 美化
  (org-modern-table nil)
  :config
  (global-org-modern-mode))

6.4. org-modern-indent

org-modern-indent 优化在 org-modern 开启下的 indent,效果很不错!

(use-package org-modern-indent
  :ensure '(org-modern-indent :type git :host github :repo "jdtsmith/org-modern-indent")
  :after org-modern
  :config ; add late to hook
  (add-hook 'org-mode-hook #'org-modern-indent-mode 90))

6.5. org-pretty-table

org-pretty-table 美化 org 的 table,它的效果实际体验来看的话,比 org-modern 好得多!

(use-package org-pretty-table
  :ensure '(org-pretty-table :type git :host github :repo "Fuco1/org-pretty-table")
  :hook (org-mode . org-pretty-table-mode))

6.6. org 导出调整

通用的导出设置:

(use-package ox
  :ensure nil
  :after org
  :custom
  (org-export-with-toc t)
  (org-export-with-drawers nil)
  (org-export-with-priority t)
  (org-export-with-footnotes t)
  (org-export-with-smart-quotes t)
  (org-export-with-section-numbers t)
  (org-export-with-sub-superscripts '{})
  (org-export-use-babel t)
  (org-export-headline-levels 9)
  (org-export-coding-system 'utf-8)
  (org-export-with-broken-links 'mark)
  (org-export-default-language "zh-CN") ; 默认是en
  (org-html-htmlize-output-type 'css)
  (org-html-head-include-default-style nil)
  :config
  ;; 很奇怪,这个变量通过 custom 设置无效,但是 setq 生效
  (setq org-export-exclude-tags '("TOC")))

;; export extra
(use-package ox-extra
  :ensure nil
  :after org
  :config
  (ox-extras-activate '(ignore-headlines)))

设置完后,我们按下 C-x C-e 键后,可以看到默认就支持了 iCalendar、HTML、LaTex、ODT、Plain Text、Publish(HTML静态站点)这几个导出格式。

6.6.1. HTML 配置

导出使用的主题可以参考这里:org mode html theme

直接在顶部加上以下代码即可:

HTML_HEAD: <link rel="stylesheet" type="text/css" href="https://gongzhitaao.org/orgcss/org.css"/>
(use-package ox-html
  :ensure nil
  :after org
  :init
  ;; add support for video
  (defun org-video-link-export (path desc backend)
    (let ((ext (file-name-extension path)))
      (cond
       ((eq 'html backend)
        (format "<video width='800' preload='metadata' controls='controls'><source type='video/%s' src='%s' /></video>" ext path))
       ;; fall-through case for everything else
       (t
        path))))
  (org-link-set-parameters "video" :export 'org-video-link-export)
  :custom
  (org-html-doctype "html5")
  (org-html-html5-fancy t)
  (org-html-checkbox-type 'unicode)
  (org-html-validation-link nil))

(use-package htmlize
  :ensure t
  :after org
  :custom
  (htmlize-pre-style t)
  (htmlize-output-type 'css))

6.6.2. 导出 PDF

org 导出 pdf,首先需要保证已经安装了 latex 相关环境,并且有 xelatex

还需要在要导出的 org 开头添加 #+LATEX_HEADER: \usepackage{ctex}

(use-package ox-latex
  :ensure nil
  :after org
  :defer t
  :config
  (setq org-latex-compiler "xelatex"))

6.7. org 链接类型

org 文件里通过 C-c C-l 来插入链接

(use-package ol
  :ensure nil
  :defer t
  :custom
  (org-link-keep-stored-after-insertion t)
  (org-link-abbrev-alist '(("github"        . "https://github.com/")
                           ("gitlab"        . "https://gitlab.com/")
                           ("google"        . "https://google.com/search?q=")
                           ("wiki"          . "https://en.wikipedia.org/wiki/")
                           ("youtube"       . "https://youtube.com/watch?v=")
                           ("zhihu"         . "https://zhihu.com/question/"))))

6.8. org-contrib org 工具

(use-package org-contrib :after org)

6.9. valign 控制对齐

(use-package valign
  :hook ((org-mode . valign-mode)
         (markdown-mode . valign-mode)))

6.10. org-appear 自动展开强调链接

当我们的光标移动到 Org mode 里的强调、链接上时,会自动展开,这样方便进行编辑。

(use-package org-appear
  :hook (org-mode . org-appear-mode))

6.11. toc-org 生成目录

为 org 生成目录。

(use-package toc-org
  :hook (org-mode . toc-org-mode))

6.12. ox-gfm 导出为 markdown

将 org 导出为 github 风格的 markdown

(use-package ox-gfm :defer t)

6.13. org-margin 突出显示标题

用于突出标题,但是不应该和 olivetti 一起使用。

(use-package org-margin
  :ensure '(org-margin :type git :host github :repo "rougier/org-margin")
  :disabled t
  :hook (org-mode . org-margin-mode))

6.14. org-fragtog 自动切换 latex 渲染

org-fragtog 当光标进入和退出时自动切换 Org 模式 LaTeX 片段预览。

(use-package org-fragtog
  :hook (org-mode . org-fragtog-mode))

6.15. org-roam

org-roam 知识管理系统

(use-package org-roam
  :after org)

6.16. init-org.el 文件尾

(provide 'init-org)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-org.el ends here

7. init-md.el

提供 markdown 支持

7.1. init-md.el 文件头

;;; init-md.el --- Org mode settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

7.2. markdown-mode

markdown-mode 专门给 markdown 使用的 mode,提供了不少功能!

(use-package markdown-mode
  :ensure t
  :mode ("README\\.md\\'" . gfm-mode)
  :defer t
  :init (setq markdown-command "pandoc"))

7.3. markdown-toc

为 markdown 生成标题,具体操作见 Github

(use-package markdown-toc
  :hook (markdown-mode . markdown-toc-mode))

7.4. init-md.el 文件尾

(provide 'init-md)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-md.el ends here

8. init-completion.el

Emacs的补全设置。

8.1. init-completion.el 文件头

;;; init-completion.el --- Completion settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

8.2. smartparens

Emacs 的次要模式,聪明地处理括号。

(use-package smartparens
  :hook (prog-mode text-mode markdown-mode) ;; add `smartparens-mode` to these hooks
  :config
  ;; load default config
  (require 'smartparens-config))

8.3. vertico 垂直补全系统

vertico 插件提供了一个垂直样式的补全系统。

(use-package
 vertico
 :defer t
 :custom
 (vertico-scroll-margin 0) ;; Different scroll margin
 (vertico-count 20) ;; Show more candidates
 (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
 (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
 :init (vertico-mode))

8.4. 提升 vertico 体验(抄自官方文档)

(use-package
 emacs
 :ensure nil
 :custom
 ;; Support opening new minibuffers from inside existing minibuffers.
 (enable-recursive-minibuffers t)
 ;; Emacs 28 and newer: Hide commands in M-x which do not work in the current
 ;; mode.  Vertico commands are hidden in normal buffers. This setting is
 ;; useful beyond Vertico.
 (read-extended-command-predicate #'command-completion-default-include-p)
 :init
 ;; Add prompt indicator to `completing-read-multiple'.
 ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
 (defun crm-indicator (args)
   (cons
    (format "[CRM%s] %s"
            (replace-regexp-in-string
             "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" "" crm-separator)
            (car args))
    (cdr args)))
 (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

 ;; Do not allow the cursor in the minibuffer prompt
 (setq minibuffer-prompt-properties
       '(read-only t cursor-intangible t face minibuffer-prompt))
 (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))

8.4.1. vertico-directory

;; Configure directory extension.
(use-package
 vertico-directory
 :after vertico
 :ensure nil
 ;; More convenient directory navigation commands
 :bind
 (:map
  vertico-map
  ("RET" . vertico-directory-enter)
  ("DEL" . vertico-directory-delete-char)
  ("M-DEL" . vertico-directory-delete-word))
 ;; Tidy shadowed file names
 :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

8.4.2. vertico-mouse

给 vertico 启动鼠标支持

(use-package vertico-mouse
  :after vertico
  :ensure nil
  :config (vertico-mouse-mode))

8.5. orderless 无序搜索

;; orderless 是一种哲学思想
(use-package
 orderless
 :defer t
 :custom
 ;; Configure a custom style dispatcher (see the Consult wiki)
 ;(orderless-style-dispatchers
 ; '(+orderless-consult-dispatch orderless-affix-dispatch))
 ;(orderless-component-separator #'orderless-escapable-split-on-space)
 (completion-styles '(orderless flex))
 (orderless-matching-styles
  '(orderless-regexp orderless-literal orderless-flex))
 (completion-category-defaults nil)
 (completion-category-overrides '((file (styles partial-completion)))))

8.6. marginalia 增加提示信息支持

(use-package
 marginalia
 :after vertico
 :config (marginalia-mode)
 :custom
 (marginalia-annotators
  '(marginalia-annotators-heavy marginalia-annotators-light nil)))

8.7. consult 配置

consult 提供查询

(use-package
  consult
  :demand t
  :bind
  ( ;; C-c bindings in `mode-specific-map'
   ("C-c M-x" . consult-mode-command)
   ("C-c h" . consult-history)
   ("C-c k" . consult-kmacro)
   ("C-c m" . consult-man)
   ("C-c i" . consult-info)
   ([remap Info-search] . consult-info)
   ([remap isearch-forward] . consult-line)
   ([remap isearch-backward] . consult-line)
   ;; C-x bindings in `ctl-x-map'
   ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
   ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
   ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
   ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
   ("C-x t b" . consult-buffer-other-tab) ;; orig. switch-to-buffer-other-tab
   ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump
   ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer
   ;; Custom M-# bindings for fast register access
   ("M-#" . consult-register-load)
   ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
   ("C-M-#" . consult-register)
   ;; Other custom bindings
   ("M-y" . consult-yank-pop) ;; orig. yank-pop
   ;; M-g bindings in `goto-map'
   ("M-g e" . consult-compile-error)
   ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
   ("M-g g" . consult-goto-line) ;; orig. goto-line
   ("M-g M-g" . consult-goto-line) ;; orig. goto-line
   ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
   ("M-g m" . consult-mark)
   ("M-g k" . consult-global-mark)
   ("M-g i" . consult-imenu)
   ("M-g I" . consult-imenu-multi)
   ;; M-s bindings in `search-map'
   ("M-s d" . consult-find) ;; Alternative: consult-fd
   ("M-s c" . consult-locate)
   ("M-s g" . consult-grep)
   ("M-s G" . consult-git-grep)
   ("M-s r" . consult-ripgrep)
   ("M-s l" . consult-line)
   ("M-s L" . consult-line-multi)
   ("M-s k" . consult-keep-lines)
   ("M-s u" . consult-focus-lines)
   ;; Isearch integration
   ("M-s e" . consult-isearch-history)
   :map
   isearch-mode-map
   ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
   ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
   ("M-s l" . consult-line) ;; needed by consult-line to detect isearch
   ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
   ;; Minibuffer history
   :map
   minibuffer-local-map
   ("M-s" . consult-history) ;; orig. next-matching-history-element
   ("M-r" . consult-history) ;; orig. previous-matching-history-element
   :map
   org-mode-map
   ("C-c C-j" . consult-org-heading))
  :hook (completion-list-mode . consult-preview-at-point-mode)
  :init
  (setq
   register-preview-delay 0.5
   register-preview-function #'consult-register-format)
  (advice-add #'register-preview :override #'consult-register-window)
  (setq
   xref-show-xrefs-function #'consult-xref
   xref-show-definitions-function #'consult-xref)
  :config
  (consult-customize
   consult-theme
   :preview-key '(:debounce 0.4 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-recent-file consult--source-project-recent-file consult--source-bookmark
   :preview-key "M-.")
  ;; 让 consult 支持预览 org 时使用 org-modern 和 olivetti-mode
  (add-to-list 'consult-preview-allowed-hooks 'global-org-modern-mode)
  (add-to-list 'consult-preview-allowed-hooks 'olivetti-mode)
  )

8.7.1. consult flycheck

为 consult 继承 flycheck,官方出品

(use-package consult-flycheck
  :after consult)

8.7.2. consult TODO

为 consult 集成 TODO 支持

(use-package consult-todo
  :after consult)

8.7.3. consult ls git

consult 的 git 扩展

(use-package consult-ls-git
  :after consult
  :bind
  (("C-c g f" . #'consult-ls-git)
   ("C-c g F" . #'consult-ls-git-other-window)))

8.7.4. consult dir

(use-package consult-dir
  :after consult
  :bind (("C-x C-d" . consult-dir)
         :map vertico-map
         ("C-x C-d" . consult-dir)
         ("C-x C-j" . consult-dir-jump-file)))

8.7.5. consult snippets

(use-package consult-yasnippet
  :after consult)

8.7.6. consult lsp

consult-lsp,给 consult 增加 lsp 支持

(use-package consult-lsp
  :after lsp consult
  :bind (:map lsp-mode-map
              ([remap xref-find-apropos] . consult-lsp-symbols)))

8.7.7. consult-gh

consult-gh,为 consult 添加 github cli 支持

(use-package consult-gh
  :if (executable-find "gh")
  :after consult)

8.8. corfu 补全框架

corfu 是一个补全框架,相较于 company 更加现代化

(use-package corfu
  :defer t
  :after savehist
  :custom
  (corfu-cycle t)
  (corfu-auto t)
  (corfu-separator ?\s)
  (corfu-preselect 'prompt)
  (corfu-scroll-margin 5)
  :bind
  (:map corfu-map
        ("TAB" . corfu-next)
        ([tab] . corfu-next)
        ("S-TAB" . corfu-previous)
        ([backtab] . corfu-previous))
  :init
  (global-corfu-mode)
  (corfu-history-mode)
  (add-to-list 'savehist-additional-variables 'corfu-history))

8.8.1. 优化 emacs 体验

(use-package emacs
  :ensure nil
  :custom
  ;; TAB cycle if there are only few candidates
  ;; (completion-cycle-threshold 3)

  ;; Enable indentation+completion using the TAB key.
  ;; `completion-at-point' is often bound to M-TAB.
  (tab-always-indent 'complete))

8.8.2. corfu-nerd-icons

corfu-nerd-icons,为 corfu 增加 nerd icons 支持

(use-package nerd-icons-corfu
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

8.9. dabbrev 配置

  ;; Use Dabbrev with Corfu!
(use-package dabbrev
  :ensure nil
  ;; Swap M-/ and C-M-/
  :bind (("M-/" . dabbrev-completion)
         ("C-M-/" . dabbrev-expand))
  :config
  (add-to-list 'dabbrev-ignored-buffer-regexps "\\` ")
  ;; Since 29.1, use `dabbrev-ignored-buffer-regexps' on older.
  (add-to-list 'dabbrev-ignored-buffer-modes 'doc-view-mode)
  (add-to-list 'dabbrev-ignored-buffer-modes 'pdf-view-mode)
  (add-to-list 'dabbrev-ignored-buffer-modes 'tags-table-mode))

8.10. cape 补全后端

(use-package
  cape
  :defer t
  :bind ("C-c p" . cape-prefix-map)
  :init
  (add-to-list 'completion-at-point-functions #'cape-file)
  ;(add-to-list 'completion-at-point-functions #'cape-abbrev)
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  (add-to-list 'completion-at-point-functions #'cape-keyword) ; programming language keyword
  (add-to-list 'completion-at-point-functions #'cape-dict)
  (add-to-list 'completion-at-point-functions #'cape-elisp-symbol) ; elisp symbol
  (add-to-list 'completion-at-point-functions #'cape-elisp-block)
  ;(add-to-list 'completion-at-point-functions #'cape-line)
  (add-hook 'completion-at-point-functions #'cape-emoji)
  (add-hook 'completion-at-point-functions #'cape-tex)
  (add-hook 'completion-at-point-functions #'cape-history)
  :config
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))

8.10.1. cape 集成 yasnippet

通过 yasnippet-capf,将 yasnippet 集成到 cape 中

(use-package yasnippet-capf
  :after cape yasnippet
  :config
  (add-to-list 'completion-at-point-functions #'yasnippet-capf))

8.11. yasnippet 补全框架

补全框架 yasnippet

(use-package yasnippet :defer t
  :config
  (yas-global-mode 1))

(use-package
 doom-snippets
 :ensure
 (doom-snippets
  :type git
  :host github
  :repo "doomemacs/snippets"
  :files ("*.el" "*"))
 :after yasnippet)

;; 再装一个通用模板库,省得没 template 用
(use-package yasnippet-snippets :after yasnippet)

8.12. init-completion.el 文件尾

(provide 'init-completion)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-completion.el ends here

9. init-dev.tl

保存的主要是版本管理工具。

9.1. init-dev.el 文件头

;;; init-dev.el --- Development settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

9.2. magit 版本管理

magit 是Emacs里的另一个杀手级应用!可以直接在Emacs里进行基于git的版本管理。

先安装依赖 transient,magit 居然没把它直接作为依赖来看(大概是因为只有 windows 需要)

(use-package transient :defer t)
(use-package magit
  :after transient)

9.3. magit-todo

在 magit 的缓冲区进行高亮。

(use-package magit-todos
  :after magit
  :config (magit-todos-mode 1))

9.4. diff-hl 高亮显示修改的部分

diff-hl 插件可以在左侧高亮显示相对于远程仓库的修改部分。

(use-package diff-hl :defer t :init (global-diff-hl-mode t))

9.5. magit-delta 增强 git diff

magit-delta 插件可以通过 git-delta 来更优化的方式显示diff内容(需要提前安装 git-delta )。

(use-package magit-delta
  :hook (magit-mode . magit-delta-mode))

9.6. magit-file-icons

magit-file-icons,为 magit 集成 文件图标

(use-package magit-file-icons
  :ensure t
  :after magit
  :init
  (magit-file-icons-mode 1))

9.7. treemacs-magit

为 treemacs 添加 magit 支持

(use-package treemacs-magit
  :after (treemacs magit))

9.8. emsg-blame

emsg-blame 便捷查看 git blame commit 信息的工具,非常好用。

(use-package emsg-blame
  :ensure
  '(emsg-blame
    :type git
    :host github
    :repo "ISouthRain/emsg-blame")
  :custom
  (emsg-blame-background nil "enable emsg blame background")
  (emsg-blame-display
   (lambda ()
     "Display git blame message, right-aligned with Magit-style faces.
If another message is already being displayed, display both messages unless they
do not both fit in the echo area."
     (let* ((message-log-max nil) ; prevent messages from being logged to *Messages*
            (cur-msg (or (current-message) ""))
            (blm-msg (format "%s %s %s "
                             emsg-blame--commit-summary
                             (propertize emsg-blame--commit-author 'face 'error)
                             (propertize emsg-blame--commit-date 'face 'warning)))
            (available-width (max 0 (- (frame-width) (string-width cur-msg) 1)))
            (blm-msg-width (string-width blm-msg))
            (padding (max 0 (- available-width blm-msg-width)))
            (rev-blm-msg (concat (make-string padding ?\s) blm-msg)))
       (if (> blm-msg-width available-width)
           (message blm-msg)
         (message (concat cur-msg rev-blm-msg))))))
  :config (global-emsg-blame-mode))

9.9. init-dev.el 文件尾

(provide 'init-dev)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-dev.el ends here

10. init-tools.el

10.1. init-tools.el 文件头

;;; init-tools.el --- Tools settings -*- lexical-binding: t -*-
;;; Commentary: Useful tools to make Emacs efficient!

;;; Code:

10.2. windows/linux 粘贴图片

先声明一个检测是 wayland 还是 xorg 的函数

(when (string= system-type "gnu/linux")
  (defun detect-display-server-via-xdg ()
    "通过 $XDG_SESSION_TYPE 检测当前桌面环境: Wayland 或 Xorg,返回相应的值。"
    (let ((session-type (getenv "XDG_SESSION_TYPE")))
      (cond
       ((string= session-type "wayland")
        t)  ;; 返回 true
       ((string= session-type "x11")
        nil) ;; 返回 false
       (t
        nil)))) ;; 返回 nil
)

windows 粘贴函数

  (if (or (eq system-type 'windows-nt)
          (eq system-type 'gnu/linux))
      (defun paste-img ()
        "paste image from clipboard"
        (interactive)
        (let* ((file-path (buffer-file-name))
               (directory (if file-path
                              (file-name-directory file-path)
                            (error "No associated file for the current buffer")))
               (image-directory (expand-file-name "image" directory))
               (timestamp (format-time-string "%Y%m%d_%H%M%S"))
               (format (completing-read "Select image form:" '("png" "jpg")))
               (image-name (format "image_%s.%s" timestamp format))
               (image-path (expand-file-name image-name image-directory))
               (image-format (if (string= format "jpg") "Jpeg" "Png"))
               (script (format "Add-Type -AssemblyName System.Windows.Forms; $clipboardImage = [System.Windows.Forms.Clipboard]::GetImage(); if ($clipboardImage -ne $null) { $clipboardImage.Save('%s', [System.Drawing.Imaging.ImageFormat]::%s); Write-Host 'Image saved'; } else { Write-Host 'No image in clipboard'; }" image-path image-format)))
         
          (unless (file-exists-p image-directory)
            (make-directory image-directory t))
           
(if (eq system-type 'gnu/linux)
(if (detect-display-server-via-xdg)
            ;; Wayland
            (progn
              (if (string= format "png")
                  (call-process "sh" nil nil nil "-c"  (format "wl-paste --type image/png > %s" image-path))
                (call-process "sh" nil nil nil "-c" (format "wl-paste --type image/png | convert - %s" image-path)))
              )
          ;; Xorg
          (progn
            (call-process "sh" nil nil nil "-c" (format "xclip -selection clipboard -t image/png -o | convert - %s" image-path))
            ))
           
          (call-process "pwsh" nil nil nil "-Command" script))
         
          (if (file-exists-p image-path)
              (progn
                (insert (format "[[file:%s]]" (concat "image/" image-name))) ; 插入正确的相对路径
                (message "Image successfully saved to: %s" image-path))
            (message "No image in clipboard or image not saved")))))

10.3. undo-fu

线性 undo 插件

(use-package undo-fu
  :bind (("C-z" . undo-fu-only-undo)   ;; 绑定 C-z 为只撤销
         ("C-S-z" . undo-fu-only-redo))) ;; 绑定 C-S-z 为只重做

10.4. vundo

可视化的撤销树

(use-package vundo
    :defer t
    :custom
    (vundo-glyph-alist vundo-unicode-symbols "beautify unicode for tree")
    :bind (("C-x u" . vundo)))

10.5. 设置 shell

(when (eq system-type 'windows-nt)
  (if (executable-find "pwsh")
      (setq explicit-shell-file-name "pwsh")))

10.6. crux

一系列比较方便使用的函数

(use-package
   crux
   :bind
   (("C-x K" . crux-kill-other-buffers)
    ("C-k" . crux-smart-kill-line)
    ("C-c r" . crux-rename-file-and-buffer))
   :config
   (crux-with-region-or-buffer indent-region)
   (crux-with-region-or-buffer untabify)
   (crux-with-region-or-point-to-eol kill-ring-save)
   (defalias 'rename-file-and-buffer #'crux-rename-file-and-buffer))

10.7. mwin

mwin 改进 C-aC-e 功能

(use-package
 mwim
 :bind
 ("C-a" . mwim-beginning-of-code-or-line)
 ("C-e" . mwim-end-of-code-or-line))

10.8. ace-window 窗口导航

ace-window 对每个 window 增加编号方便跳转!

(use-package ace-window :bind (("C-x o" . 'ace-window)))

10.9. helpful帮助增强

helpful 插件提供了帮助增强。

(use-package helpful
  :commands (helpful-callable helpful-variable helpful-command helpful-key helpful-mode)
  :bind (([remap describe-command] . helpful-command)
         ("C-h f" . helpful-callable)
         ("C-h F" . helpful-function)
         ("C-h v" . helpful-variable)
         ("C-c C-d" . heloful-at-point)
         ("C-h x" . helpful-command)
         ([remap describe-key] . helpful-key)))

10.10. projectile

projectile,Emacs 的项目导航和管理库

(use-package projectile
  :init
  (projectile-mode +1)
  :bind (:map projectile-mode-map
              ("C-c p" . projectile-command-map)))

10.11. init-tools.el 文件尾

(provide 'init-tools)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-tools.el ends here

11. init-lsp.el

lsp 配置,目前主流的 lsp client 也就是 lsp-mode eglot lsp-bridge

  • 其中 eglot 属于 emacs 内置,无需额外安装,但是确定很明显,它功能不够,并且因为纯 elisp 实现,速度不快
  • lsp-mode 也是纯 elisp 实现,不过更完善,包含自动安装 lsp server 的功能,但更慢,并且由于功能过于复杂,一部分功能可能缺乏维护!
  • lsp-bridge 则是通过 python 来提高速度,并且支持绝大多数的 server ,并且是国人制作!

11.1. init-lsp.el 文件头

;;; init-lsp.el --- Development settings -*- lexical-binding: t -*-
;;; Commentary:

;;; Code:

11.2. lsp-mode

(use-package lsp-mode
  :commands (lsp lsp-deferred)
  :custom
  (lsp-keymap-prefix "C-c l")  ;; Or 'C-l', 's-l'
  (lsp-idle-delay 0.500)
  ;; 这会禁用掉某些无用的东西
  (lsp-completion-provider :none)
  :hook ((prog-mode . (lambda ()
                        (unless (or (derived-mode-p 'lisp-mode 'emacs-lisp-mode))
                          (lsp-deferred))))))

11.2.1. lsp-ui

lsp-ui,精致的 lsp ui 功能!

(use-package lsp-ui :commands lsp-ui-mode)

11.2.2. lsp-treemacs

lsp-treemacs,将 lsp-mode 集成到 treemacs 中

(use-package lsp-treemacs
  :after treemacs lsp-mode
  :config
  (lsp-treemacs-sync-mode 1))

11.3. lsp-pyright

添加 lsp-pyright 支持,lsp-pyright

可以使用 pyright 和 basedpytight

(use-package lsp-pyright
  :hook (python-mode . (lambda ()
                         (require 'lsp-pyright)
                         (lsp))))  ; or lsp-deferred

11.4. lsp-bridge 配置

lsp-bridge 是一个由 python 实现的 lsp client。

lsp-bridge 的目标是使用多线程技术实现 Emacs 生态中速度最快的 LSP 客户端, 开箱即用的设计理念, 节省你自己折腾的时间, 时间就是金钱。

lsp-bridge 的优势:

  1. 速度超快: 把 LSP 的请求等待和数据分析都隔离到外部进程, 不会因为 LSP Server 返回延迟或大量数据触发 GC 而卡住 Emacs
  2. 远程补全: 内置远程服务器代码补全, 支持密码、 公钥等多种登录方式, 支持 tramp 协议, 支持 SSH 多级堡垒机跳转, 支持 Docker
  3. 开箱即用: 安装后立即可以使用, 不需要额外的配置, 不需要自己折腾补全前端、 补全后端以及多后端融合等配置
  4. 多服务器融合: 只需要一个简单的 JSON 即可混合多个 LSP Server 为同一个文件提供服务, 例如 Python, Pyright 提供代码补全, Ruff 提供诊断和格式化
  5. 灵活的自定义: 自定义 LSP Server 选项只需要一个 JSON 文件即可, 简单的几行规则就可以让不同的项目使用不同 JSON 配置

需要 python 依赖:

  • epc
  • orjson
  • sexpdata
  • six
  • setuptools
  • rapidfuzz

当前该插件处于关闭状态

(use-package
  lsp-bridge
  :defer t
  :disabled t
  :ensure
  '(lsp-bridge
    :type git
    :host github
    :repo "manateelazycat/lsp-bridge"
    :files
    (:defaults "*.el" "*.py" "acm" "core" "langserver" "multiserver" "resources")
    :build (:not compile))
  :init (global-lsp-bridge-mode))

11.5. init-lsp.el 文件尾

(provide 'init-lsp)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-lsp.el ends here

12. init-treesitter.el

12.1. init-treesitter.el 文件头

;;; init-treesitter.el --- Tools settings -*- lexical-binding: t -*-
;;; Commentary: Useful tools to make Emacs efficient!

;;; Code:

12.2. treesit-auto

treesit-auto 是一个自动安装 treesitter 的插件,但是需要对应的 lang 有相应的 ts-mode

(use-package treesit-auto
  :custom
  (treesit-auto-install 'prompt)
  (treesit-auto-install t)
  (treesit-font-lock-level 4)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

12.3. ts-fold

利用 treesitter 进行代码折叠。

(use-package ts-fold
  :defer t
  :ensure (ts-fold :type git :host github :repo "emacs-tree-sitter/ts-fold"))

12.4. init-treesitter.el 文件尾

(provide 'init-treesitter)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-treesitter.el ends here

13. init-lang.el

13.1. init-lang.el 文件头

;;; init-lang.el --- Tools settings -*- lexical-binding: t -*-
;;; Commentary: Useful tools to make Emacs efficient!

;;; Code:

13.2. web-mode

web-mode,增加前端支持

(use-package web-mode
  :mode
  (("\\.phtml\\'" . web-mode)
   ("\\.php\\'" . web-mode)
   ("\\.tpl\\'" . web-mode)
   ("\\.[agj]sp\\'" . web-mode)
   ("\\.as[cp]x\\'" . web-mode)
   ("\\.erb\\'" . web-mode)
   ("\\.mustache\\'" . web-mode)
   ("\\.djhtml\\'" . web-mode)))

13.2.1. vue-mode

vue-mode,增加 vue 支持

(use-package vue-mode)

13.3. lua-mode

lua-mode,增加 lua 支持

(use-package lua-mode)

13.4. go-mode

go-mode,增加 go 支持

(use-package go-mode)

13.5. zig-mode for ziglang

zig-mode 是一个由 ziglang 官方维护的包。

(use-package
 zig-mode
 :defer t
 :custom (zig-format-on-save nil "disable zig format on save"))

13.6. elisp lang

highlight-defined

高亮 elisp 中已经定义的符号

(use-package highlight-defined :hook (elisp-mode . highlight-defined-mode))

13.7. nix-mode for nixlang

nix-mode 是由 nix 官方维护的次要模式

(use-package nix-mode
  :if (not (eq system-type 'windows-nt))
  :mode "\\.nix\\'")

13.8. rust-mode

rust-mode,官方维护的 rust-mode

(use-package rust-mode
  :defer t
  :init
  (setq rust-mode-treesitter-derive t)
  :hook
  (rust-mode . (lambda () (prettify-symbols-mode))))

13.8.1. rustic

(use-package rustic
  :after (rust-mode)
  :config
  (setq rustic-format-on-save nil)
  :custom
  (rustic-cargo-use-last-stored-arguments t))

13.9. init-lang.el 文件尾

(provide 'init-lang)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-lang.el ends here

14. init-blog.el

14.1. init-blog.el 文件头

;;; init-blog.el --- blog settings -*- lexical-binding: t -*-
;;; Commentary: Useful tools to make Emacs efficient!

;;; Code:

14.2. ox-hugo

ex-hugo 可以便捷地帮助我们使用 org 来编写博客内容。

(use-package ox-hugo
  :custom
  (org-hugo-auto-set-lastmod t "auto update latest time")
  )

14.2.1. 增加文章创建功能

先定义一个模板

(defun format-org-hugo-header (title author date base-dir section categories layout export-file-name)
  "Format an Org-mode header for ox-hugo with the given parameters."
  (format "#+TITLE: %s\n#+AUTHOR: %s\n#+DATE: %s\n#+HUGO_BASE_DIR: %s\n#+HUGO_SECTION: %s\n#+HUGO_CUSTOM_FRONT_MATTER: :math false\n#+HUGO_CATEGORIES: %s\n#+HUGO_LAYOUT: %s\n#+EXPORT_FILE_NAME: %s\n"
          title
          author
          date
          base-dir
          section
          categories
          layout
          export-file-name))
(defun blog-org()
  "Create a new blog post org in the org-hugo-base-dir."
  (interactive)
  (let* ((article-type (read-string "input article type:"))
         (article-name (read-string "input article name:"))
         (article-lang (completing-read "select language: " '("en" "cn"))) 
         (layout (completing-read "select layout: " '("docs" "blog" "default")))
         (base-dir "~/blog")
         (current-time (current-time))
         (year (format-time-string "%Y" current-time))
         (month (format-time-string "%m" current-time))
         (day (format-time-string "%d" current-time))
         (date (concat year "-" month "-" day))
         (section (concat article-type "/" year "/" month "/" day "/" article-name))
         (post-dir (expand-file-name (concat "content-org" "/" section) base-dir))
         (file-name (concat "index" (if (string= article-lang "en") "" ".zh-cn") ".org"))
         (index-file (expand-file-name file-name post-dir)))
    (make-directory post-dir t)
    (with-temp-file index-file
      (insert (format-org-hugo-header article-name "" date base-dir section "" layout file-name)))  ;; 确保这个函数存在并返回有效内容
    (find-file index-file)  ;; 在文件生成后打开
    (message "Create file: %s" index-file)))

14.3. init-blog.el 文件尾

(provide 'init-blog)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-blog.el ends here

15. init-ai.el

15.1. init-ai.el 文件头

;;; init-ai.el --- blog settings -*- lexical-binding: t -*-
;;; Commentary: Useful tools to make Emacs efficient!

;;; Code:

15.2. copilot

copilot,copilot 支持

(use-package copilot
  :ensure (:host github :repo "copilot-emacs/copilot.el" :files ("*.el"))
  :bind
  (("C-c n" . copilot-next-completion)
   ("C-c p" . copilot-previous-completion)
   ("C-c w" . copilot-accept-completion-by-word)
   ("C-c l" . copilot-accept-completion-by-line)
   ("C-c RET" . rk/copilot-complete-or-accept))
  :config
  (defun rk/copilot-complete-or-accept ()
    "Command that either triggers a completion or accepts one if one
is available. Useful if you tend to hammer your keys like I do."
    (interactive)
    (if (copilot--overlay-visible)
        (progn
          (copilot-accept-completion)
          (open-line 1)
          (next-line))
      (copilot-complete)))
  (add-to-list 'copilot-indentation-alist '(prog-mode 2))
  (add-to-list 'copilot-indentation-alist '(org-mode 2))
  (add-to-list 'copilot-indentation-alist '(text-mode 2))
  (add-to-list 'copilot-indentation-alist '(closure-mode 2))
  (add-to-list 'copilot-indentation-alist '(emacs-lisp-mode 2)))

15.3. copilot-chat

copilot-chat 是一第三方实现的插件,支持使用 copilot

(use-package copilot-chat
  :ensure (:host github :repo "chep/copilot-chat.el" :files ("*.el"))
  :custom
  (copilot-chat-curl-program "curl")
  :after (request org))

15.4. init-ai.el 文件尾

(provide 'init-ai)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-ai.el ends here

作者: 金中甲

Created: 2024-10-04 Fri 16:12