1 Star 0 Fork 5

战栗/Emacs configuration

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
emacs-config.org 83.90 KB
一键复制 编辑 原始数据 按行查看 历史

#+title: Emacs configuration

初始代码

garbage collection, lexical binding

;; -*- lexical-binding: t; -*-
(setq gc-cons-threshold (* 50 1000 1000))

禁止Emacs写入自定义变量

(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) 

org-babel

将本文件内容写进 init.el 等文件中, 并自动保存.

(defun my/org-babel-tangle-config ()
  (when (string-equal (buffer-file-name)
                      (expand-file-name "emacs-config.org" user-emacs-directory))
    ;; Dynamic scoping to the rescue
    (let ((org-confirm-babel-evaluate nil))
      (org-babel-tangle))))

(add-hook 'org-mode-hook
          (lambda () (add-hook 'after-save-hook #'my/org-babel-tangle-config)))

Android 系统初始化

;; 针对 Emacs 30 Android 端口的设置
(setq my/is-android (eq system-type 'android))
(when my/is-android
  (setenv "PATH" (format "%s:%s" "/data/data/com.termux/files/usr/bin:/data/data/com.termux/files/usr/bin/texlive" (getenv "PATH")))
  (setenv "LD_LIBRARY_PATH" (format "%s:%s"
                                    "/data/data/com.termux/files/usr/lib"
                                    (getenv "LD_LIBRARY_PATH")))
  (push "/data/data/com.termux/files/usr/bin" exec-path)
  (push "/data/data/com.termux/files/usr/bin/texlive" exec-path)
  (setenv "TEXMFROOT" "/data/data/com.termux/files/usr/share/texlive"))

Straight 初始化

;; 在执行 init.el 前禁用 package.el
(setq package-enable-at-startup nil)

straight 主页上下载的 straight 初始化代码. 注意 Emacs 29 更改了 native compilation 有关的变量. 切换到 develop 分支能暂时解决这个问题

(defvar bootstrap-version)
;; 修复 Emacs 29 修改了 native-compile 相关变量导致的 bug
(unless (version<= emacs-version "28.2")
  (setq straight-repository-branch "develop"))
(setq straight-check-for-modifications '(check-on-save find-when-checking))

(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 6))
  (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))

use-package

在以后我们的 use-package 代码块其实是 straight-use-package. 这样我们 use-package 设置在安装 straight 和未安装 straight 的机器上能够最大程序地保持一致.

(straight-use-package 'use-package) ; 安装 use-package, 版本29后内置
(setq straight-use-package-by-default t) ; 自动安装所有插件, 相当于加入 :straight t
(setq use-package-always-defer t) ; 总是延迟加载
;; 如果不使用 straight, 上面一行相当于:
;; (setq use-package-always-ensure t) ; 相当于加入 :ensure t

general.el

(use-package general
  :defer 1
  :config
  (general-evil-setup t)

  (general-create-definer
    my/leader-def
    :states '(normal visual insert emacs)
    :prefix "SPC"
    :global-prefix "<f1> SPC"
    :keymap 'override)
  (general-create-definer
    my/insert-leader-def
    :states '(insert emacs)
    :global-prefix "<f1> SPC"
    :keymap 'override)
  (my/insert-leader-def
    "SPC" '(cycle-spacing :which-key "cycle spacing"))) 

临时文件与最近文件

临时文件 (no-littering)

no-littering 保持文件夹整洁

(use-package no-littering
  :demand t
  :custom
  (auto-save-file-name-transforms `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))) ; 设置自动保存文件目录

最近文件 (recentf)

recentf 快速打开最近文件

(use-package recentf
  :after no-littering
  :demand t 
  :custom
  (recentf-exclude '(no-littering-var-directory
                     no-littering-etc-directory)) ; 屏蔽临时文件
  (recentf-max-menu-items 25)
  (recentf-max-saved-items 25)
  :bind ("C-x C-r" . 'recentf-open-files)
  :config
  (recentf-mode 1))

一些全局设置

本地化编译

;; Emacs 29 启用了新变量名
(if (version<= emacs-version "28.2")
    (setq native-comp-deferred-compilation-deny-list '(".*pdf.*")) ; 禁用 =pdf-tools= 有关文件的本地化编译
  (setq native-comp-jit-deferred-compilation-deny-list '(".*pdf.*"))) ; 禁用 =pdf-tools= 有关文件的本地化编译

错误信息

;; (setq warning-minimum-level :error) ; 不提示 warning 

默认模式

(setq-default major-mode 'text-mode)

检测系统与环境

这一段包含个人不同设备的设置.

识别系统类型

(setq my/is-windows (eq system-type 'windows-nt)) ; Windows 系统
(setq my/is-linux (eq system-type 'gnu/linux)) ; Linux 系统
(setq my/is-manjaro (string-match "MANJARO" operating-system-release)) ; manjaro 系统
(setq my/is-termux (string= system-configuration "aarch64-unknown-linux-android")) ; termux
(setq my/is-userland (string= system-configuration "aarch64-unknown-linux-gnu")) ; userland
(setq my/is-WSL
      (and (eq system-type 'gnu/linux)
           (string-match
            "icrosoft"
            (shell-command-to-string "uname -a"))))
(setq my/is-HW-Mbook ; HW Matebook
(and (eq system-type 'gnu/linux)
           (string-match
            "Y2023"
            (shell-command-to-string "uname -a"))))

;; 注意: my/is-android 已经在 early-init.el 中定义了.

不常用/不稳定功能

;; 如果想开启这些功能可以直接把该变量设为 1. 这里默认的判断条件是随意选取的.
(setq my/is-turn-on-experimental (or (file-exists-p "~/.dotfiles/tangle-dotfiles.el")
                                     (file-exists-p "~/termux-home/")))

图形界面

(setq my/is-terminal (not window-system))

模块加载

Server

(server-start)

历史记录与自动保存

命令历史记录

(use-package savehist
  :defer 1
  :config (savehist-mode))

保存光标位置

(use-package saveplace
  :defer 1
  :config
  (save-place-mode 1))

自动保存文件

(use-package super-save
  :defer 1
  :custom
  (super-save-auto-save-when-idle t)
  :config
  (super-save-mode +1))

自动更新 buffer

(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers nil)

编码, 字体及输入法

编码

(prefer-coding-system 'utf-8) ; 默认编码
(setq-default buffer-file-coding-system 'utf-8-unix) ; 默认 EOL 设置

字体大小变量

(defvar my/font-height 130)
(defvar my/latex-preview-scale 1.3)

(defvar my/mm-char-height ; 字体高度 (mm)
  (if (or my/is-termux my/is-android my/is-HW-Mbook) 2.4 4.2)) ; 平板和笔记本电脑的字体小一些.
;; 计算达到上述文字大小, 相应的字体大小.
;; font-height (in 1/10 pixel) = 10 * total pixel / (totol length in mm / preset height in mm)
;; 当字体高度为 4.2 mm 时, 对应的大致字体大小为 1080p: 15.5; 2K: 18; 4K: 22
(defun my/get-font-height (&optional frame)
  (let* ((attrs (frame-monitor-attributes frame))
         (geometry (alist-get 'geometry attrs)) 
         (size (alist-get 'mm-size attrs)) 
         (pixel-width (caddr geometry)) ; 第三个参数是宽度
         (mm-width ; 第一个参数是宽度
          (cond (my/is-android (/ (car size) 100)) ; 平板上宽度获取有问题
                (my/is-HW-Mbook 300)
                (t (car size)))))
         (round (* 10 (/ pixel-width  (/ mm-width my/mm-char-height))))))

  (defun my/set-font-size ()
    ;; 用 my/get-font-height 设置 my/font-height 与 my/latex-preview-scale 的值.
    ;; 一般是 80:1 的关系 (数学公式略大). 只有 Android 上的 Emacs 是 2:1
    (interactive)
    (let* ((font-size (my/get-font-height)))
      (message "font size: %s" font-size)
      (setq my/font-height font-size)
      (setq my/latex-preview-scale
            (if my/is-android (/ font-size 2.0)
              (/ font-size 80.0)))))

字体设置

(defun my/set-font (font-height &optional frame)
  (interactive)
  (unless (or my/is-terminal); 非图形界面不能使用函数 set-fontset-font
    ;; 设置系统默认字体
    (setq my/system-default-font 
          (if my/is-android "Droid Sans Mono" (font-get-system-normal-font)))
    ;; 设置 Emacs 默认字体
    (setq my/default-font "Fira Code") ; 包名: fonts-firacode (ubuntu) 或 ttf-fira-code (manjaro)
    (unless (find-font (font-spec :name my/default-font))
      (message (format "cannot find %s for the default font" my/default-font))
      (setq my/default-font my/system-default-font))

    ;; 设置 LaTeX 代码默认字体
    (setq my/math-font
          (if my/is-android "DejaVu Math TeX Gyre" ; 平板上数学字体
            "Latin Modern Math")) ; texlive 自带字体
    (unless (find-font (font-spec :name my/math-font))
      (message (format "cannot find %s for the math font. Use system default instead"  my/math-font))
      (setq my/math-font my/system-default-font))


    (setq my/chinese-font
          (cond (my/is-termux "Roboto")
                (my/is-windows "Kaiti")
                ;; (my/is-linux "AR PL KaitiM GB") ; fonts-arphic-gkai00mp (apt)
                (t "AR PL UKai CN"))) ; 包名: ttf-arphic-ukai (manjaro)
    (unless (find-font (font-spec :name my/chinese-font))
      (message (format "cannot find %s for the chinese font. Use system default instead"  my/chinese-font))
      (setq my/chinese-font my/system-default-font))

    (setq my/variable-pitch-font
          (if my/is-windows "Segoe Print"
            "Iosevka Aile")) ; 包名: ttc-iosevka-aile (manjaro)
    (unless (find-font (font-spec :name my/variable-pitch-font))
      (message (format "cannot find %s for the variable-pitch font. Use system default instead"  my/variable-pitch-font))
      (setq my/variable-pitch-font my/system-default-font))


    (setq my/fixed-pitch-font "JetBrains Mono") ; 包名: fonts-jetbrains-mono (ubuntu) 或 ttf-jetbrains-mono (manjaro)
    (unless (find-font (font-spec :name my/fixed-pitch-font))
      (message (format "cannot find %s for the fixed-pitch font. Use system default instead"  my/fixed-pitch-font))
      (setq my/fixed-pitch-font my/system-default-font))

    (set-face-attribute 'default frame :font my/default-font :height font-height)  ; 默认字体及字号.
    (set-face-attribute 'variable-pitch frame :font my/variable-pitch-font :height font-height) ; 比例字体 
    (set-face-attribute 'fixed-pitch frame :font my/fixed-pitch-font :height font-height) ; 等宽字体 

    (set-fontset-font "fontset-default" 'mathematical my/math-font) ; 数学符号默认字体
    (set-fontset-font "fontset-default" 'han my/chinese-font) ; 中文默认字体
    (setq inhibit-compacting-font-caches t) ; 汉字及 Unicode 显示问题
    (setq auto-window-vscroll nil))) ; 不根据行高做上下滚动调整

(defun my/set-font-current-frame ()
  ;; 根据当前显示器尺寸设置字体大小
  (interactive)
  (my/set-font (my/get-font-height) (selected-frame)))
(global-set-key (kbd "C-x 9") #'my/set-font-current-frame)

中文输入法 (pyim)

pyim 自定义探针函数

(defun my/pyim-probe-latex-mode ()
  "latex-mode 中的数学环境自动切换到英文输入."
  (and (eq major-mode 'latex-mode)
       (if (fboundp 'texmathp) (texmathp) nil)))

pyim 设置

(use-package popup) ; 选词框
(use-package pyim-wbdict
  :defer 2) ; 五笔输入法
(use-package pyim 
  :defer 2
  :after pyim-wbdict
  :bind
  ("M-j" . 'pyim-convert-string-at-point)
  :config
  (setq default-input-method "pyim")
  (setq pyim-default-scheme 'wubi)

  ;; 使用从 http://gaokuan.ysepan.com/?xzpd=1 上下载的五笔词库.
  (pyim-extra-dicts-add-dict
   `(:name "my-wubi-dict" :file
           ,(file-truename (expand-file-name "lisp/my-wubi-86-dict.pyim" user-emacs-directory))))
  (require 'popup)
  (setq pyim-page-tooltip '(minibuffer)) ; 需要候选框可以使用 popup, 但是会影响速度
  (setq-default pyim-punctuation-translate-p '(no)); 总是输入半角标点. 
  (setq-default pyim-english-input-switch-functions
                '(pyim-probe-auto-english
                  pyim-probe-isearch-mode
                  pyim-probe-program-mode
                  pyim-probe-org-structure-template
                  pyim-probe-org-latex-mode
                  my/pyim-probe-latex-mode))
  (setq-default pyim-punctuation-half-width-functions
                '(pyim-probe-punctuation-line-beginning
                  pyim-probe-punctuation-after-punctuation)))

输入顿号

(defun my/insert-pause ()
  (interactive)
  (insert "、"))
(my/insert-leader-def
  "," '(my/insert-pause :which-key "insert pause"))

界面

主题

试用新主题的函数

参考 https://www.reddit.com/r/emacs/comments/ezetx0/doomthemes_screenshots_updated_good_time_to_go/

(defun my/load-doom-theme (theme)
  "Disable active themes and load a Doom theme."
  (interactive (list (intern (completing-read "Theme: "
                                              (->> (custom-available-themes)
                                                   (-map #'symbol-name)
                                                   (--select (string-prefix-p "doom-" it)))))))
  (ap/switch-theme theme)
  (set-face-foreground 'org-indent (face-background 'default)))

(defun my/switch-theme (theme)
  "Disable active themes and load THEME."
  (interactive (list (intern (completing-read "Theme: "
                                              (->> (custom-available-themes)
                                                   (-map #'symbol-name))))))
  (mapc #'disable-theme custom-enabled-themes)
  (load-theme theme 'no-confirm))

Doom 主题

(use-package doom-themes
  :if (not my/is-termux) ;; disable doom-themes in termux
  :demand t
  :config
  (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
        doom-themes-enable-italic t) ; if nil, italics is universally disabled
  (load-theme 'doom-moonlight t) ; 当前主题
  (doom-themes-visual-bell-config) ; Enable flashing mode-line on errors
  (doom-themes-org-config))

时间

(setq display-time-day-and-date t)
(display-time-mode 1)

moe theme

(use-package moe-theme
  :demand t
  :if my/is-termux
  :config 
  (moe-dark))

doom mode-line

(use-package all-the-icons
  :if (display-graphic-p)) ; 第一次运行时, M-x all-the-icon-install-fonts 安装字体.
(use-package minions
  :hook (doom-modeline-mode . minions-mode))
(use-package doom-modeline
  :hook (after-init . doom-modeline-mode)
  :custom
  (doom-modeline-unicode-fallback t)
  :config
  (setq doom-modeline-height 1) ; optional
  (custom-set-faces
   '(mode-line ((t (:height 0.85))))
   '(mode-line-active ((t (:height 0.85)))) ; For 29+
   '(mode-line-inactive ((t (:height 0.85))))))

行号, 列号与换行

这个要在 doom-theme 之后才能生效!

(defun my/set-line-number ()
  (interactive)
  (column-number-mode) ; 显示列号
  (global-visual-line-mode t) ; 自动断行
  (global-display-line-numbers-mode t) ; 显示行号
  ;; 不显示行号的模式:
  (dolist (mode '(term-mode-hook
                  helpful-mode-hook
                  eshell-mode-hook
                  pdf-view-mode-hook
                  eww-mode-hook))
    (add-hook mode (lambda () (display-line-numbers-mode 0))))

  (setq display-line-numbers-type 'visual) ; 行号考虑自动断行
  ;; 行号强制用等宽字体
  (unless my/is-terminal
    (set-face-attribute 'line-number nil :font my/default-font :height 0.9) 
    (set-face-attribute 'line-number-current-line nil :font my/default-font :height 0.9)))

窗口外观

(setq default-frame-alist '((fullscreen . maximized))) ; 全屏
(add-to-list 'default-frame-alist '(alpha . (90 . 90) ))
(defun my/set-window ()
  (interactive)
  (unless my/is-termux ; 终端下以下设置不可用
    (tool-bar-mode 0) ; 禁用工具栏
    (scroll-bar-mode 0)) ; 禁用滚动条
  (menu-bar-mode 0) ; 禁用菜单栏
  )

折叠

(setq my/enable-folding (not my/is-terminal))

窗口管理

默认打开新窗口方式

(setq split-height-threshold nil) ; 默认左右分屏
(setq display-buffer-base-action
      '((display-buffer-reuse-window ; 针对 *eshell* 等重名 buffer
         display-buffer-reuse-mode-window ; 默认使用当前窗口的模式
         display-buffer-in-previous-window ; 在用过的窗口中打开
         display-buffer-same-window))) ; 在当前窗口中打开

弹出窗口管理 (popper)

(when my/is-turn-on-experimental
  (setq my/right-popper-buffer-regexp "\\(\\*scratch\\|\\*draft\\|\\*help\\|\\*Message\\|\\*Customize\\)") ; 在右侧打开的临时窗口
  (setq my/bottom-popper-buffer-regexp "\\(\^\\*shell\\|\*compilation\\|\\*Backtrace\\|\\*Warnings\\|output\\*$\\)") ; 在下方打开的临时窗口

  (use-package popper
    :defer 2
    :after perspective ; use perspective to group
    ;; 修正了 popper-echo.el 中一个 bug. 若不同时使用 echo-mode 与 group-function 也可以使用原有版本(删去下一行)
    :straight (:local-repo "../../lisp/popper/" :type nil) 
    :bind (("C-M-'" . popper-toggle-latest)
           ("M-'" .  popper-cycle)
           ("C-M-;" . popper-toggle-type))
    :custom
    (popper-reference-buffers `(,my/bottom-popper-buffer-regexp ,my/right-popper-buffer-regexp))
    (popper-group-function #'popper-group-by-perspective) ; 用 perspective 分类弹出窗口
    (popper-display-control nil) ; 用 display-buffer-list 管理弹出窗口行为
    :config
    ;; 在 termux 中不能使用 C-', 在 EXWM 窗口中不能使用 C-M. 这里定义备用快捷键
    (my/leader-def
      "pl"  'popper-toggle-latest 
      "pt"  'popper-toggle-type 
      "pc"  'popper-cycle)
    (popper-mode +1)
    (popper-echo-mode +1))
  )

新窗口显示管理

设置 display-buffer-alist 变量

弹出窗口管理与 dsiplay-buffer-alist 初始化

(setq window-side-slots '(0 0 2 2)) ; 左上右下最大临时窗口数
(setq display-buffer-alist
      `((,my/bottom-popper-buffer-regexp ; 下侧打开的窗口
         (display-buffer-in-previous-window display-buffer-reuse-mode-window display-buffer-in-side-window)
         (window-height . 0.2)
         (window-min-height . 7)
         (side . bottom)
         (reusable-frames . visible))
        (,my/right-popper-buffer-regexp ; 右侧打开的窗口
         (display-buffer-reuse-mode-window display-buffer-in-side-window)
         (reusable-frames . visible)
         (side . right)
         (direction . rightmost)
         (window-width . 0.33))))

LaTeX 相关窗口管理 (pdftex)

(if (not my/is-android)
    (progn  (add-to-list 'display-buffer-alist '(".*pdf$" ; 默认右侧打开 pdf
                                                 (display-buffer-in-direction
                                                  display-buffer-in-previous-window)
                                                 (direction . rightmost)))
            (add-to-list 'display-buffer-alist '(".*tex$" (display-buffer-in-previous-window)))); 默认在原来的窗口中打开 tex 文件
  (progn
    (add-to-list 'display-buffer-alist '(".*pdf$" (display-buffer-in-previous-window))))) 

org-roam 相关窗口管理

;; org-roam 默认在右侧打开
(add-to-list 'display-buffer-alist
             '("\\*org-roam\\*"
               (display-buffer-in-direction)
               (direction . right)
               (window-width . 0.33)
               (window-height . fit-window-to-buffer)))

org-capture 相关窗口管理

;; org-roam capture 与 *Org-Select* 默认右侧打开
(add-to-list 'display-buffer-alist '("\\(^CAPTURE.*\.org$\\|\\*Org.*Select\\*$\\)"
                                     (display-buffer-in-side-window)
                                     (side . right)
                                     (slot . 0)
                                     (window-width . 60)))

快速窗口切换

(use-package ace-window
  :defer 2
  :bind ("M-o" . ace-window)
  :custom
  (aw-scope 'global)
  (aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
  :config
  (ace-window-display-mode 1))

窗口布局历史记录 (Winner mode)

(use-package winner
  :defer 2
  :config
  (winner-mode))

补全 (minibuffer)

Vertico

(defun my/minibuffer-backward-kill (arg)
  "When minibuffer is completing a file name delete up to parent
  folder, otherwise delete a word"
  (interactive "p")
  (if minibuffer-completing-file-name
      ;; Borrowed from https://github.com/raxod502/selectrum/issues/498#issuecomment-803283608
      (if (string-match-p "/." (minibuffer-contents))
          (zap-up-to-char (- arg) ?/)
        (delete-minibuffer-contents))
    (delete-word (- arg))))
(setq completion-ignore-case 't) ; minibuffer 补全忽略大小写
(use-package vertico
  :defer 1
  :custom
  (verticle-cycle t)
  :config
  (vertico-mode)
  :bind (:map minibuffer-local-map
              ("M-h" .  my/minibuffer-backward-kill)))

额外补全信息 (Marginalia)

(use-package marginalia
  ;; Either bind `marginalia-cycle' globally or only in the minibuffer
  :bind (("M-A" . marginalia-cycle)
         :map minibuffer-local-map
         ("M-A" . marginalia-cycle))
  :defer 1
  :config
  (marginalia-mode))

乱序补全 (Orderless)

(use-package orderless
  :defer 1
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion)))))

帮助信息优化

快捷键提示 (which-key)

(use-package which-key
  :defer 3
  :custom (which-key-idle-delay 1)
  :config (which-key-mode))

帮助界面优化 (helpful)

(use-package helpful
  :defer 3
  :bind ; 这里更好的选择可能是用 [remap]
  (([remap describe-function] . #'helpful-callable)
   ([remap describe-variable] . #'helpful-variable)
   ([remap describe-key] . #'helpful-key)
   ([remap describe-command] . #'helpful-command)
   ([remap describe-symbol] . #'helpful-symbol)
   ("C-h C-d" . #'helpful-at-point)
   ("C-h F" . #'helpful-function))
  :config
  (add-hook 'helpful-mode-hook 'visual-line-mode)) ; 侧窗显示的帮助界面需要自动换行

当前按键显示 (keycast)

(use-package keycast
  :demand t
  :after doom-modeline
  :config
  (keycast-header-line-mode 1)) ; 默认在 header 显示

文本编辑

彩色括号 (rainbow-delimiters)

(use-package rainbow-delimiters
  :hook (rainbow-delimiters-mode . prog-mode)) ; 在编程模式中启用

prettify 设置

;; (setq prettify-symbols-unprettify-at-point my/enable-folding) ; 光标附近自动展开

文本补全 (company mode)

(use-package company
  :hook ((org-mode LaTeX-mode prog-mode) . company-mode) ; 加载 company 的主模式
  :custom
  (company-minimum-prefix-length 4)
  (company-idle-delay 0.3)
  (company-tootip-idle-delay 0.5)
  (company-tooltip-offset-display 'line)
  (company-tooltip-align-annotation t)
  (company-show-quick-access t)
  (company-backends
   '((company-capf :with company-dabbrev-code company-keywords)
     (company-dabbrev)
     (company-ispell)
     (company-files)))
  ;; dabbrev 不改变大小写
  (company-dabbrev-ignore-case nil) 
  (company-dabbrev-downcase nil)
  (company-transformers '(company-sort-by-occurrence company-sort-by-backend-importance))
  (company-show-quick-access 'left)
  :bind
  (:map company-active-map 
        ("M-/" . company-complete)
        ("<tab>" . company-indent-or-complete-common)
        ("C-c C-/" . company-other-backend))
  :config
  (set-face-attribute 'company-tooltip nil :inherit 'fixed-pitch))

跳转 (avy-jump)

(use-package avy 
  :demand 1
  :after general
  :config
  (my/leader-def
    "j" '(:ignore t :which-key "jump")
    "jj" '(avy-goto-char :which-key "jump to char")
    "jw" '(avy-goto-word-0 :which-key "jump to word")
    "jl" '(avy-goto-line :which-key "jump to line")))

Vim 快捷键 (evil)

(use-package key-chord
  :demand t
  :custom
  (key-chord-two-keys-delay 0.5) ; 在 evil 模式中可以用 "jk" 切换到普通模式
  :config
  (key-chord-mode 1)
  (defun my/restart-keychord-mode ()
    (interactive)
    (key-chord-mode 0)
    (key-chord-mode 1)))

(use-package evil
  :defer 1
  :after key-chord
  :init
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil) ; 使用 evil-collection 的快捷键
  (setq evil-respect-visual-line-mode t) ; j/k 键考虑 visual line; 必须在 evil 加载前设置
  :custom
  (evil-move-cursor-back nil)  
  (evil-move-beyond-eol t)
  (evil-want-fine-undo t)
  :config
  (key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
  (evil-mode 1))

(use-package evil-collection
  :after evil
  :demand t
  :config
  ;; (setq evil-collection-mode-list nil)
  ;; (evil-collection-init '(company compile consult debug diff-hl diff-mode dired dired-sidebar elisp-mode embark eshell ewww helm help helpful lua-mode (magit magit-repos magit-submodule) markdown-mode org org-present org-roam (pdf pdf-view) popup python reftex sh-script (term term ansi-term multi-term) vertico whick-key yaml-mode)))
  (evil-collection-init))

evil-org

org 模式中使用 evil

(use-package evil-org
  :after org
  :hook (org-mode . evil-org-mode)
  :config
  (require 'evil-org-agenda)
  (evil-org-agenda-set-keys)
  (general-define-key
   :states '(normal motion)
   :keymaps 'org-mode-map
   "zd" 'org-fold-hide-drawer-toggle)
  )

拼写检查

ispell 词典设置

;; 个人词典
(setq my/enchant-personal-dic-en (file-truename "~/.config/enchant/en_US.dic"))
(unless (file-exists-p my/enchant-personal-dic-en)
  (setq my/enchant-personal-dic-en nil))
(setq ispell-personal-dictionary my/enchant-personal-dic-en)

;; 主要用于 company-ispell 的拼写补全字典
(setq my/completion-dic-en (file-truename "~/.config/english-words.txt")) ; 主要用于 company-ispell
(if (file-exists-p my/completion-dic-en) ; 这里用 file-truename 转换成绝对路径!
    (setq ispell-alternate-dictionary (file-truename my/completion-dic-en)
          ispell-complete-word-dict (file-truename my/completion-dic-en)))

flyspell

需要安装 aspellhunspell. Windows 可通过 msys2 安装 libenchant 及相关字典


(use-package flyspell
  :if (or my/is-windows)
  :hook (LaTeX-mode org-mode))

Jinx

https://github.com/minad/jinx

(unless (or my/is-windows ; windows 下 jinx 无法编译
            (not my/is-turn-on-experimental))
  (use-package jinx
    :hook (emacs-startup . global-jinx-mode)
    :bind ([remap ispell-word] . jinx-correct) ; 把拼写检查绑定到 flyspell 的 C-$ 上.
    :config 
    (add-to-list 'jinx-exclude-regexps '(t "\\cc")) ; 拼写检查忽略中文
    (add-to-list 'jinx-exclude-faces
                 '(latex-mode font-lock-constant-face ; 不会对 LaTeX 中 label 进行拼写检查.
                              font-lock-comment-face))))

其它设置

Hydra

(use-package hydra)

临时文件目录

防止 Windows<user name>~1 命名家目录, 修复一个 preview-latex 的 bug

(if (eq system-type 'windows-nt)
    (setq temporary-file-directory "~/AppData/Local/Temp/"))

安全的文件变量

(setq safe-local-variable-values
      '((code . utf-8)
        (eval setq-local org-roam-db-location (file-truename "./org-roam.db")) ; 允许多个 org-roam 目录
        (eval setq-local org-roam-directory (file-truename "./"))))

LaTeX 设置

cdlatex

cdlatex 自定义变量

补全命令模板

(defun my/set-cdlatex-command-alist ()
  (setq cdlatex-command-alist
        '(("eq" "insert pairs of \\[ \\]" "\\[ ? \\]" cdlatex-position-cursor nil t t)
          ("Big(" "insert Big ()" "\\Big( ? \\Big" cdlatex-position-cursor nil nil t)
          ("Big[" "insert Big[" "\\Big[ ? \\Big" cdlatex-position-cursor nil nil t)
          ("Big\\|" "insert Big \\|" "\\Big\\| ? \\Big\\|" cdlatex-position-cursor nil nil t)
          ("Big{" "insert Big{}" "\\Big\\{ ? \\Big\\" cdlatex-position-cursor nil nil t)
          ("Big|" "insert Big|" "\\Big| ? \\Big|" cdlatex-position-cursor nil nil t)
          ("aali" "insert equation" "\\left\\{\\begin{aligned}\n? \n\\end{aligned}\\right." cdlatex-position-cursor nil nil t)
          ("alb" "Insert beamer alert block with overlay" "\\begin{alertblock}<+->{ ? } \n\n\\end{alertblock}" cdlatex-position-cursor nil t nil)
          ("alb*" "Insert beamer alert block without overlay" "\\begin{alertblock}{ ? } \n\n\\end{alertblock}" cdlatex-position-cursor nil t nil)
          ("big(" "insert big ()" "\\big( ? \\big" cdlatex-position-cursor nil nil t)
          ("big[" "insert big []" "\\big[ ? \\big" cdlatex-position-cursor nil nil t)
          ("big\\|" "insert big \\|" "\\big\\| ? \\big\\|" cdlatex-position-cursor nil nil t)
          ("bigg(" "insert bigg()" "\\bigg( ? \\bigg" cdlatex-position-cursor nil nil t)
          ("bigg[" "insert bigg[" "\\bigg[ ? \\bigg" cdlatex-position-cursor nil nil t)
          ("bigg\\|" "insert bigg\\|" "\\bigg\\| ? \\bigg\\|" cdlatex-position-cursor nil nil t)
          ("bigg{" "insert bigg{}" "\\bigg\\{ ? \\bigg\\" cdlatex-position-cursor nil nil t)
          ("bigg|" "insert bigg|" "\\bigg| ? \\bigg|" cdlatex-position-cursor nil nil t)
          ("big{" "insert big {}" "\\big\\{ ? \\big\\" cdlatex-position-cursor nil nil t)
          ("big|" "insert big|" "\\big| ? \\big|" cdlatex-position-cursor nil nil t)
          ("blo" "Insert beamer block with overlay" "\\begin{block}<+->{ ? } \n\n\\end{block}" cdlatex-position-cursor nil t nil)
          ("blo*" "Insert beamer block WITHOUT overlay" "\\begin{block}{ ? } \n\n\\end{block}" cdlatex-position-cursor nil t nil)
          ("bn" "binomial" "\\binom{?}{}" cdlatex-position-cursor nil nil t)
          ("capl" "insert \\bigcap\\limits_{}^{}" "\\bigcap\\limits_{?}^{}" cdlatex-position-cursor nil nil t)
          ("case" "insert cases" "\\begin{cases}\n? & \\\\\n &\n\\end{cases}" cdlatex-position-cursor nil nil t)
          ("cd" "insert cdots" "\\cdots" nil nil t t)
          ("cupl" "insert \\bigcup\\limits_{}^{}" "\\bigcup\\limits_{?}^{}" cdlatex-position-cursor nil nil t)
          ("dd" "insert ddots" "\\ddots" nil nil t t)
          ("def" "insert definition env" "" cdlatex-environment ("definition") t nil)
          ("des" "insert description" "" cdlatex-environment ("description") t nil)
          ("enu*" "insert enu" "\\begin{enumerate}\n\\item ?\n\\end{enumerate}" cdlatex-position-cursor nil t nil)
          ("equ*" "insert unlabel equation" "" cdlatex-environment ("equation*") t nil)
          ("exb" "Insert beamer example block with overlay" "\\begin{exampleblock}<+->{ ? } \n\n\\end{exampleblock}" cdlatex-position-cursor nil t nil)
          ("exb*" "Insert beamer example block without overlay" "\\begin{exampleblock}{ ? } \n\n\\end{exampleblock}" cdlatex-position-cursor nil t nil)
          ("exe" "Insert exercise" "\\begin{exercise}\n? \n\\end{exercise}" cdlatex-position-cursor nil t nil)
          ("fra" "insert frame (for beamer)" "" cdlatex-environment ("frame") t nil)
          ("hhl" "insert \\ \\hline" "\\\\ \\hline" ignore nil t nil)
          ("hl" "insert \\hline" "\\hline" ignore nil t nil)
          ("ipenu" "insert in paragraph enumerate" "" cdlatex-environment ("inparaenum") t nil)
          ("ipite" "insert in paragraph itemize" "" cdlatex-environment ("inparaitem") t nil)
          ("it" "insert \\item" "\\item?" cdlatex-position-cursor nil t nil)
          ("ld" "insert ldots" "\\ldots" nil nil t t)
          ("lem" "insert lemma env" "" cdlatex-environment ("lemma") t nil)
          ("liml" "insert \\lim\\limits_{}" "\\lim\\limits_{?}" cdlatex-position-cursor nil nil t)
          ("lr<" "insert bra-ket" "\\langle ? \\rangle" cdlatex-position-cursor nil nil t)
          ("myenu" "insert in my enumerate for beamer" "" cdlatex-environment ("myenumerate") t nil)
          ("myite" "insert in my itemize for beamer" "" cdlatex-environment ("myitemize") t nil)
          ("ons" "" "\\onslide<?>{ }" cdlatex-position-cursor nil t t)
          ("pa" "insert pause" "\\pause" ignore nil t nil)
          ("pro" "insert proof env" "" cdlatex-environment ("proof") t nil)
          ("prodl" "insert \\prod\\limits_{}^{}" " \\prod\\limits_{?}^{}" cdlatex-position-cursor nil nil t)
          ("prop" "insert proposition" "" cdlatex-environment ("proposition") t nil)
          ("se" "insert \\{\\}" "\\{ ? \\}" cdlatex-position-cursor nil nil t)
          ("spl" "insert split" "" cdlatex-environment ("split") nil t)
          ("st" "stackrel" "\\stackrel{?}{}" cdlatex-position-cursor nil nil t)
          ("te" "insert text" "\\text{?}" cdlatex-position-cursor nil nil t)
          ("thm" "insert theorem env" "" cdlatex-environment ("theorem") t nil)
          ("vd" "insert vdots" "\\vdots" nil nil t t))))

环境模板

(defun my/set-cdlatex-env-alist ()
  (setq cdlatex-env-alist
        '(("definition" "\\begin{definition}\n\\label{def:?}\n\n\\end{definition}" nil)
          ("enumerate" "\\begin{enumerate}[?]\n\\item \n\\end{enumerate}" "\\item ?")
          ("equation*" "\\begin{equation*}\n? \n\\end{equation*}" nil)
          ("exercise" "\\begin{exercise}[?]\n\n\\end{exercise}" nil)
          ("frame" "\\begin{frame}{ ? }\n\n\\end{frame}" nil)
          ("inparaenum" "\\begin{inparaenum}\n\\item ? \n\\end{inparaenum}" "\\item ?")
          ("inparaitem" "\\begin{inparaitem}\n\\item ?\n\\end{inparaitem}" "\\item ?")
          ("lemma" "\\begin{lemma}\n\\label{lem:?}\n\n\\end{lemma}" nil)
          ("myenumerate" "\\begin{myenumerate}\n\\item ?\n\\end{myenumerate}" "\\item ?")
          ("myitemize" "\\begin{myitemize}\n\\item ?\n\\end{myitemize}" "\\item ?")
          ("proof" "\\begin{proof}?\n\n\\end{proof}" nil)
          ("proposition" "\\begin{proposition}\n\n\\end{proposition}" nil)
          ("theorem" "\\begin{theorem}\n\\label{thm:?}\n\n\\end{theorem}" nil))))

数学字体修饰设置

(defun my/set-cdlatex-math-modify-alist ()
    (setq cdlatex-math-modify-alist
          '((?t "\\mathbb" "" t nil nil))))

数学符号

(defun my/set-cdlatex-math-symbol-alist ()
        (setq cdlatex-math-symbol-alist
              '((?0 ("\\varnothing" "\\emptyset"))
                (?1 ("\\ONE" "\\one"))
                (?. ("\\cdot" "\\circ"))
                (?v ("\\vee" "\\bigvee"))
                (?& ("\\wedge" "\\bigwedge"))
                (?9 ("\\cap" "\\bigcap" "\\bigoplus"))
                (?+ ("\\cup" "\\bigcup" "\\oplus"))
                (?- ("\\rightharpoonup" "\\hookrightarrow" "\\circlearrowleft"))
                (?= ("\\equiv" "\\Leftrightarrow" "\\Longleftrightarrow"))
                (?~ ("\\sim" "\\approx" "\\propto"))
                (?L ("\\Lambda" "\\limits"))
                (?* ("\\times" "\\otimes" "\\bigotimes"))
                (?e ("\\eps" "\\epsilon" "\\exp\\Big( ? \\Big)"))
                (?> ("\\mapsto" "\\longrightarrow" "\\rightrightarrows"))
                (?< ("\\preceq" "\\leftarrow" "\\longleftarrow"))
                (?| ("\\parallel" "\\mid" "\\perp"))
                (?S ("\\Sigma" "\\sum_{?}^{}"))
                (?{ ("\\subset" "\\prec" "\\subseteq"))
                (?} ("\\supset" "\\succ" "\\supseteq")))))

use-package 模块

(use-package cdlatex
  :straight (:host github :repo "cdominik/cdlatex" )
  :defer 10
  :after tex ; cdlatex 需要 auctex 中的 texmathp.el
  :config
  ;; 导入 cdlatex 自定义设置
  (setq cdlatex-paired-parens "$[{(")
  (my/set-cdlatex-math-symbol-alist)
  (my/set-cdlatex-math-modify-alist)
  (my/set-cdlatex-env-alist)
  (my/set-cdlatex-command-alist))

LaTeX 编辑

主要使用 AucTeX 及配套插件

AucTeX 变量设置

环境折叠

(defun my/TeX-fold-config ()
  (setq TeX-fold-type-list '(env macro comment)
        TeX-fold-env-spec-list '(("[comment]" ("comment")) ("[proof]" ("proof")))
        LaTeX-fold-env-spec-list '(("frame" ("frame")))
        TeX-fold-macro-spec-list
        '(("[f]" ("footnote" "marginpar"))
          ("[c]" ("cite"))
          ("[l]" ("label"))
          ("[r]" ("ref" "pageref" "eqref"))
          ("[i]" ("index" "glossary"))
          ("[1]:||*" ("item"))
          ("..." ("dots"))
          ("(C)" ("copyright"))
          ("(R)" ("textregistered"))
          ("TM" ("texttrademark"))
          (1 ("emph" "textit" "textsl" "textmd" "textrm" "textsf" "texttt" "textbf" "textsc" "textup")))))

数学字体

C-x C-e <key> 改变选中文字的字体.

(defun my/TeX-fonts-config ()
  (setq LaTeX-font-list
        '((?m "\\textmc{" "}" "\\mathmc{" "}")
          (?g "\\textgt{" "}" "\\mathgt{" "}")
          (?e "\\en{" "}")
          (?c "\\cn{" "}")
          (?4 "$" "$")
          (1 "" "" "\\mathcal{" "}")
          (2 "\\textbf{" "}" "\\mathbf{" "}")
          (3 "\\textsc{" "}")
          (5 "\\emph{" "}")
          (6 "\\textsf{" "}" "\\mathsf{" "}")
          (9 "\\textit{" "}" "\\mathit{" "}")
          (12 "\\textulc{" "}")
          (13 "\\textmd{" "}")
          (14 "\\textnormal{" "}" "\\mathnormal{" "}")
          (18 "\\textrm{" "}" "\\mathrm{" "}")
          (19 "\\textsl{" "}" "\\mathbb{" "}")
          (20 "\\texttt{" "}" "\\mathtt{" "}")
          (21 "\\textup{" "}")
          (23 "\\textsw{" "}")
          (4 "" "" t))))

preview-latex 设置

(defun my/preview-latex-config ()
  (setq preview-default-option-list
        '("displaymath" "floats" "graphics" "textmath" "footnotes") ; 执行预览的环境
        preview-preserve-counters t ; 保留数学公式编号
        preview-pdf-color-adjust-method 'compatible)) ; 预览图片使用Emacs主题背景色

reftex

(defun my/reftex-config ()
  (setq reftex-label-alist ; 交叉引用的自定义类型
        '((nil ?e nil "\\cref{%s}" nil nil) ; 与 cref 配合使用. 
          ("theorem" ?t "thm:" nil t ("Theorem" "定理"))
          ("proposition" ?p "prop:" nil t ("Proposition" "命题"))
          ("definition" ?d "def:" nil t ("Definition" ))
          ("lemma" ?a "lem:" nil t ("Lemma" "引理")))
        reftex-insert-label-flags '("s" "sftpd")
        reftex-ref-macro-prompt nil ; ~cte<tab>~ 后不提示类型
        reftex-ref-style-default-list '("Cleveref"))) ; 默认引用风格 Used to "Default"

Prettify

查找Unicode编码: https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode

(defun my/more-prettified-symbols ()
  (require 'tex-mode) ; 载入 tex--prettify-symbols-alist 变量
  (mapc (lambda (pair) (delete pair tex--prettify-symbols-alist))
        '(("\\supset" . 8835)))
  (mapc (lambda (pair) (cl-pushnew pair tex--prettify-symbols-alist))
        '(("\\big(" . #x2987) ; Notation left image bracket
          ("\\big)" . #x2988)
          ("\\Big(" . #x2985) ; left white parenthesis
          ("\\Big)" . #x2986)
          ("\\bigg(" . #xFF5F) ; full width left white parenthesis
          ("\\bigg)" . #xFF60)
          ("\\big[" . #x3010) ; full width left square bracket
          ("\\big]" . #x3011)
          ("\\Big[" . #x27E6) ; math left white square bracket
          ("\\Big]" . #x27E7)
          ("\\bigg[" . #x301A) ; left white square bracket
          ("\\bigg]" . #x301B)
          ("\\{" . #xFF5B) ; full width curly bracket
          ("\\}" . #xFF5D) 
          ("\\big\\{" . #xFF5B) ; 
          ("\\big\\}" . #xFF5D)
          ("\\Big\\{" . #xFF5B) ; white bracket
          ("\\Big\\}" . #xFF5D)
          ("\\bigg\\{" . #xFF5B) ; white bracket
          ("\\bigg\\}" . #xFF5D)
          ("\\Z" . 8484)
          ("\\Q" . 8474)
          ("\\N" . 8469)
          ("\\R" . 8477)
          ("\\eps" . 949)
          ("\\inf" . #x22C0) 
          ("\\sup". #x22C1)
          ("\\ONE" . #x1D7D9)
          ("\\mathbb{S}" . #x1D54A)
          ("\\PP" . #x2119)
          ("\\Ps" . #x1D5AF )
          ("\\Pp" . #x1D40F)
          ("\\E" . #x1D5A4)
          ("\\Ee" . #x1D404)
          ("\\EE" . #x1D53C )
          ("\\Fc" . #x2131)
          ("\\Nc" . #x1D4A9))))

大纲 (outline-minor-mode)

(use-package outline
  :init
  (if my/is-windows
      (setq outline-minor-mode-prefix (kbd "C-o"))
    (setq outline-minor-mode-prefix (kbd "C-'")))
  :bind
  (:map outline-minor-mode-map
        ("C-' t" . outline-hide-body)
        ("C-' e" . outline-show-entry)
        ("C-' p" . outline-previous-visible-heading)
        ("C-' n" . outline-next-visible-heading)
        ("C-' a" . outline-show-all)))

LaTeX-mode-hook

(defun my/latex-hook ()
  (turn-on-cdlatex) 
  (turn-on-reftex) 
  (outline-minor-mode) ; 大纲预览
  (outline-hide-body) ; 启动时折叠文件
  (when my/enable-folding
    (prettify-symbols-mode t)))  ; prettify 数学符号

pdf-tools 设置

(defun pdf-util-frame-scale-factor () 2)
(use-package pdf-tools
  :mode ("\\.pdf\\'" . pdf-view-mode) ; pdf 文件默认打开方式
  :bind
  (:map pdf-view-mode-map
        ("d" . pdf-view-next-page-command)
        ("a" . pdf-view-previous-page-command)
        ("s" . pdf-view-scroll-up-or-next-page)
        ("w" . pdf-view-scroll-down-or-previous-page)
        :map pdf-history-minor-mode-map
        ("b" . pdf-history-backward)
        :map pdf-annot-minor-mode-map
        ("C-a a" . pdf-annot-add-highlight-markup-annotation)
        ("C-a s" . pdf-annot-add-squiggly-markup-annotation)
        ("C-a u" . pdf-annot-add-underline-markup-annotation)
        ("C-a d" . pdf-annot-delete))
  :config
  (pdf-tools-install)
  (require 'pdf-annot) ; 设置 pdf-annot-mimor-mode-map 必须
  (require 'pdf-history) ; 设置 pdf-history-minor-mode-map 必须
  (add-hook 'pdf-view-mode-hook 'pdf-view-fit-width-to-window) ; 默认适应页宽
  )

数学公式字体

(defun my/set-latex-font ()
  (interactive)
  (require 'font-latex)
(unless my/is-terminal
  (set-face-attribute 'font-latex-math-face nil :foreground "#f78c6c" :font my/math-font :height 1.15)) ; 数学符号
  (set-face-attribute 'font-latex-script-char-face nil :foreground "#c792ea") ; 上下标字符^与_
  (set-face-attribute 'font-latex-sedate-face nil :foreground "#ffcb6b")) ; 关键字

use-package 模块

(use-package tex
  :defer 10
  :straight auctex
  :custom
  (TeX-parse-self t) ; 自动解析 tex 文件found
  (TeX-PDF-mode t) 
  (TeX-DVI-via-PDFTeX t)
  ;; (TeX-PDF-from-DVI "Dvips") ; for preview latex, see https://www.reddit.com/r/emacs/comments/bsoko7/comment/eor96mj/
  ;; 正向与反向搜索设置
  (TeX-source-correlate-mode t)
  (TeX-source-correlate-method 'synctex)
  (TeX-view-program-selection '((output-pdf "PDF Tools"))) ; 使用 pdf-tools 预览 pdf
  (TeX-source-correlate-start-server t)
  :config
  ;; 设置 LaTeX 语法高亮颜色及字体大小
  (setq-default TeX-master t) ; 默认询问主文件
  (add-hook 'LaTeX-mode-hook 'my/latex-hook) ; 加载LaTeX模式设置
  (add-hook 'TeX-after-compilation-finished-functions
            #'TeX-revert-document-buffer) ; 编译后更新 pdf 文件
  (my/TeX-fold-config)
  (my/TeX-fonts-config)
  (my/preview-latex-config)
  (my/reftex-config)
  (my/more-prettified-symbols))

pdf 预览

(use-package auctex-latexmk
  :straight (auctex-latexmk :fetcher github :repo "emacsmirror/auctex-latexmk") ; fix a incompatibility bug by non-author.
  :defer 5
  :after tex
  :init
  (auctex-latexmk-setup)
  :config
  (setq auctex-latexmk-inherit-TeX-PDF-mode t)
  (add-to-list 'TeX-command-list
               '("myLatexMK" "latexmk %(-PDF)%S%(mode) %(file-line-error) -pvc %(extraopts) %t" TeX-run-latexmk nil (plain-tex-mode latex-mode doctex-mode) :help "Run LatexMK with -pvc")))

Org 设置

外观

org-modern

(use-package org-modern-indent
  :straight (:host github :repo "jdtsmith/org-modern-indent")
  :config
  (add-hook 'org-mode-hook #'org-modern-indent-mode 90))

(use-package org-modern ;; 安卓下 Droid Sans Mono 字体不能显示 bullet. 最好安装 Iosevka Aile 字体.
  :custom
  (org-modern-hide-stars nil) ; adds extra indentation
  (org-modern-table nil)
  (org-modern-list 
   '((?- . "•")
     (?* . "•")
     (?+ . "•")))
  :init
  (global-org-modern-mode))

自动隐藏与显示标记 (org-appear)

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

字体 (org-face 相关)

(defun my/set-org-font ()
  (interactive)
  ;; org 字体美化
  (require 'org-faces)
  ;; 标题字体大小优化
  (set-face-attribute 'org-document-title nil :weight 'bold :height 1.2)
  (dolist (face '((org-level-1 . 1.05)
                  (org-level-2 . 1.0)
                  (org-level-3 . 1.0)
                  (org-level-4 . 1.0)
                  (org-level-5 . 1.0)
                  (org-level-6 . 1.0)
                  (org-level-7 . 1.0)
                  (org-level-8 . 1.0)))
    (set-face-attribute (car face) nil :weight 'medium :height (cdr face)))

  (set-face-attribute 'org-block nil :foreground 'unspecified' :inherit 'fixed-pitch)
  (set-face-attribute 'org-block-begin-line nil :foreground 'unspecified' :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-block-end-line nil :foreground 'unspecified' :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-property-value nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-code nil   :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-verbatim nil  :inherit '(shadow fixed-pitch))
  (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)
  (set-face-attribute 'org-drawer nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-document-info-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
  (set-face-attribute 'org-table nil :inherit 'fixed-pitch)
  (setq org-fontify-quote-and-verse-blocks t) ; 启用 org-qoute 变量为 quote 设置不同的字体
  (set-face-attribute 'org-quote nil :inherit 'fixed-pitch)
  (require 'org-indent) ;; 开启 org-indent 并设设置缩进字体
  (set-face-attribute 'org-indent nil :inherit '(org-hide fixed-pitch)))

页面宽度 (visual-fill-column)

(defun my/org-mode-visual-fill ()
(interactive)
  (setq visual-fill-column-width 150
        visual-fill-column-center-text t)
  (visual-fill-column-mode 1))
(use-package visual-fill-column
  :hook (org-mode . my/org-mode-visual-fill))

LaTeX 语法高亮相关

(defun my/org-view-latex ()
  (interactive)
  (setq org-format-latex-options
        `(:foreground default :background default :scale ,my/latex-preview-scale :html-foreground "Black" :html-background "Transparent" :html-scale ,my/latex-preview-scale :matchers ("begin" "$1" "$" "$$" "\\(" "\\[")) ; 公式预览设置
        ;; 以下为 LaTeX 语法高亮设置
        org-highlight-latex-and-related '(native latex entities)
        org-pretty-entities my/enable-folding
        org-pretty-entities-include-sub-superscripts nil))

数学公式预览 (org-preview)

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

;; 快速编译数学公式, 测试版
(use-package org-preview
  :straight (:host github :repo "karthink/org-preview")
  :hook (org-mode . org-preview-mode))
;; 在 ~/texmf/tex/latex/ 下的 .sty 文件
(setq org-latex-packages-alist '(("" "mypreamble" t)
                                 ("" "mysymbol" t)))

org-tempo 模板设置

(defun my/org-tempo-setting ()
  (interactive)
  (require 'org-tempo) ; 保证 org-structure-template-alist 可用
  (setq org-structure-template-alist ; 用 org-tempo 快速插入代码块
        '(("el" . "src elisp")
          ("la" . "src latex")
          ("sh" . "src shell")
          ("a" . "export ascii")
          ("c" . "center")
          ("C" . "comment")
          ("e" . "example")
          ("E" . "export")
          ("h" . "export html")
          ("l" . "export latex")
          ("q" . "quote")
          ("s" . "src")
          ("v" . "verse"))))

org 中截图 (org-download)

(unless my/is-android
  ;; 用文件名作为文件夹
  (defun my/org-download-method (link) 
    (let ((filename
           (file-name-nondirectory
            (car (url-path-and-query
                  (url-generic-parse-url link)))))
          (dirname (concat "./img/" (file-name-sans-extension (file-name-nondirectory (buffer-file-name))))))
      (setq org-download-image-dir dirname)
      (make-directory dirname t)
      (expand-file-name (funcall org-download-file-format-function filename) dirname)))

  ;; 在 Windows 系统下修复过时的convert.exe; 注意: 用户名文件夹不能含有空格!
  (defun my/org-download-clipboard-windows ()
    (interactive)
    (let ((filename (expand-file-name "screenshot.png" temporary-file-directory)))
      (shell-command-to-string (format "magick clipboard: %s" filename))
      (when (file-exists-p filename)
        (org-download-image filename)
        (delete-file filename))))

  ;; 在 WSL 中复制 Windows 剪贴板图片
  (defun my/org-download-clipboard-WSL ()
    (interactive)
    (let ((filename (expand-file-name "screenshot.png" "/tmp/")))
      (shell-command-to-string (format "/mnt/c/msys64/mingw64/bin/magick.exe clipboard: %s" filename)) ; 这是我的 magick.exe 安装位置
      (when (file-exists-p filename)
        (org-download-image filename)
        (delete-file filename))))

  (defun my/org-download-clipboard ()
    (interactive)
    (cond (my/is-windows (my/org-download-clipboard-windows))
          (my/is-WSL (my/org-download-clipboard-WSL))
          (t (org-download-clipboard)))) ; for Linux system

  (use-package org-download
    :custom
    (org-download-heading-lvl 1) ; 以一级标题作为图片文件夹
    (org-download-method #'my/org-download-method)
    :after org
    :bind (:map org-mode-map
                ("C-c i y" . org-download-yank)
                ("C-c i d" . org-download-delete)
                ("C-c i e" . org-download-edit)
                ("C-M-y" . my/org-download-clipboard))))

org-table 中文对齐 (valign)

(use-package valign
  :if (display-graphic-p)
  :after org
  :hook (org-mode . valign-mode))

个人小功能

在同一窗口打开 org 文件

(defun my/follow-link-at-current-window () 
  (interactive)
  (let ((org-link-frame-setup (quote ((vm . vm-visit-folder-other-frame)
                                      (vm-imap . vm-visit-imap-folder-other-frame)
                                      (gnus . gnus)
                                      (file . find-file)
                                      (wl . wl-other-frame)))))

    (org-open-at-point)))
(defun my/follow-link-at-current-window-mouse (event)
  (interactive (list last-command-event))
  (posn-set-point (event-end event))
  (let ((org-link-frame-setup (quote ((vm . vm-visit-folder-other-frame)
                                      (vm-imap . vm-visit-imap-folder-other-frame)
                                      (gnus . gnus)
                                      (file . find-file)
                                      (wl . wl-other-frame)))))
    (org-open-at-point)))

org-cdlatex-mode 中输入成对 $ 及括号

(defun my/insert-inline-OCDL ()
  (interactive)
  (insert "\\(")
  (save-excursion (insert "\\)" )))
(defun my/insert-dollar-OCDL ()
  (interactive)
  (insert "$")
  (save-excursion (insert "$" )))
(defun my/insert-bra-OCDL ()
  (interactive)
  (insert "(")
  (save-excursion (insert ")" )))
(defun my/insert-sq-bra-OCDL ()
  (interactive)
  (insert "[")
  (save-excursion (insert "]" )))
(defun my/insert-curly-bra-OCDL ()
  (interactive)
  (insert "{")
  (save-excursion (insert "}" )))
(defun my/insert-backquote ()
  (interactive)
  (insert "`"))


(defun my/set-org-cdlatex-extra-kbd ()
(interactive)
  (my/insert-leader-def
   :keymaps 'org-cdlatex-mode-map
   "$" '(my/insert-inline-OCDL :which-key "insert \\\( \\\)")
   "`" '(my/insert-backquote :which-key "insert `"))
  (general-define-key
   :states '(insert emacs)
   :keymaps 'org-cdlatex-mode-map
   "$"  'my/insert-inline-OCDL
   "("  'my/insert-bra-OCDL
   "["  'my/insert-sq-bra-OCDL
   "{"  'my/insert-curly-bra-OCDL))

org-mode-hook

(defun my-org-hook ()
  (org-indent-mode) ; 自动缩进
  (unless (or (< (display-pixel-height) 999) my/is-terminal)
    (variable-pitch-mode 1)) ; 比例字体
  (org-cdlatex-mode) ; LaTeX 公式
  (visual-line-mode 1))

use-package 模块

(use-package org
  :defer 10
  :custom
  (org-M-RET-may-split-line t)
  (org-priority-lowest ?E) ; org-agenda 的优先级设为A-E
  (org-priority-default ?D) ; org-agenda 的默认优先级设为D
  ;; (org-startup-with-latex-preview t) ; 设为 t 则创建新笔记时会出错.
  :bind
  (:map org-mode-map
        ("C-c p" . nil) ; 用于 popper 快捷键
        ("C-c n" . nil) ; 用于 org-roam 快捷键
        ("C-c o" . my/follow-link-at-current-window) ; 在当前窗口打开 org 文件
        ("C-<down-mouse-1>" . my/follow-link-at-current-window-mouse) ; Ctrl+鼠标点击时, 在当前窗口打开 org 文件
        ("C-<drag-mouse-1>" . my/follow-link-at-current-window-mouse))
  :config
  (unless my/is-android
    (require 'org-download)
    (setq org-ellipsis " ▾")); 用小箭头代替...表示折叠
  (if t ; my/enable-folding
      (setq org-startup-folded 'content) ; 开启时折叠大纲
    (setq org-startup-folded 'showeverything))

  (unless my/is-terminal
    (my/set-org-font))
  (my/org-view-latex)
  (my/org-tempo-setting)
  (my/set-org-cdlatex-extra-kbd)
  (add-hook 'org-mode-hook 'my-org-hook)
  (add-to-list 'org-babel-load-languages '(shell . t)))

幻灯片展示 (org-present)

(use-package org-present
  :config
  (defun my/org-present-prepare-slide (buffer-name heading)
    (org-overview)  ; 仅显示顶层标题Show only top-level headlines
    (org-show-entry); 展开当前标题Unfold the current entry
    (org-show-children))   ; 显示当前子标题

  (defun my/org-present-start () ; 开始幻灯片的设置
    (turn-off-evil-mode)
    (setq visual-fill-column-width 110
          visual-fill-column-center-text t) ; 调整显示界面
    ;; 调整字体大小
    (setq-local face-remapping-alist '((default (:height 1.5) variable-pitch)
                                       (header-line (:height 4.0) variable-pitch)
                                       (org-document-title (:height 1.75) org-document-title)
                                       (org-code (:height 1.55) org-code)
                                       (org-verbatim (:height 1.55) org-verbatim)
                                       (org-block (:height 1.25) org-block)
                                       (org-block-begin-line (:height 0.7) org-block)))
    (setq header-line-format " ") ; 在标题前加入空行
    (display-line-numbers-mode 0)
    (org-display-inline-images) ; 显示图片
    (read-only-mode 1)) ; 只读模式

  (defun my/org-present-end () ; 重置上述设置
    (setq-local face-remapping-alist 
                '((default variable-pitch default)))      
    (setq header-line-format nil) 
    (org-remove-inline-images)
    (org-present-small)
    (read-only-mode 0)
    (display-line-numbers-mode 1)
    (turn-on-evil-mode))


  (add-hook 'org-present-mode-hook 'my/org-present-start)
  (add-hook 'org-present-mode-quit-hook 'my/org-present-end)
  (add-hook 'org-present-after-navigate-functions 'my/org-present-prepare-slide))

org-ref

(use-package org-ref
  :bind (:map org-mode-map
              ("C-c (". org-ref-insert-label-link)
              ("C-c )". org-ref-insert-ref-link)))

org-transclusion

    (use-package org-transclusion
  :straight (:host github
                       :repo "nobiot/org-transclusion")
:after org)

个人知识库 (org-roam)

许多设置参考了 https://www.youtube.com/watch?v=CUkuyW6hr18 及相关教程

变量设置

(setq zot_bib "~/Nutstore/1/Nutstore/Zotero-Library/Main.bib"; Zotero .bib 文件
      zot_pdf "~/Nutstore/1/Nutstore/Zotero-Library" ; Zotero 同步文件夹
      org_notes "~/repos/notes/ref/") ; org-roam 文献笔记目录

(unless (file-exists-p zot_bib) (setq zot_bib nil))
(unless (file-exists-p zot_pdf) (setq zot_pdf nil))
(unless (file-exists-p org_notes) (setq org_ntoes nil)) ; 防止文件不存在而报错

(setq my/daily-note-filename "%<%Y-%m-%d>.org" ; 日记默认文件名
      my/daily-note-header "#+title: %<%Y-%m-%d %a>\n#+SETUPFILE: ~/repos/notes/latex-preamble.org\n\n[[roam:%<%Y-%B>]]\n\n") ; 日记文件头

use-package 模块

(use-package org-roam
  :defer 15
  :custom
  (org-roam-directory "~/repos/notes/") ; 默认笔记目录
  (org-roam-completion-everywhere t)
  (org-roam-node-display-template ; 搜索节点信息显示
   (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
  (org-roam-db-gc-threshold most-positive-fixnum)
  (org-roam-dailies-directory "daily/") ; 默认日记目录
  (org-roam-dailies-capture-templates ; 日记默认模板
   `(("d" "default" entry "* %?" ; 普通条目
      :target (file+head ,my/daily-note-filename
                         ,my/daily-note-header))
     ("t" "task" entry "* TODO %?\n  %U\n  %a\n  %i" ; 待办
      :if-new (file+head+olp ,my/daily-note-filename
                             ,my/daily-note-header
                             ("Tasks"))
      :empty-lines 1) 
     ("j" "journal" entry "* %<%I:%M %p> - Journal  :journal:\n\n%?\n\n" ; 研究日志
      :if-new (file+head+olp ,my/daily-note-filename
                             ,my/daily-note-header
                             ("Log")))
     ("m" "meeting" entry "* %<%I:%M %p> - Meeting with %^{whom}  :meetings:\n\n%?\n\n" 
      :if-new (file+head+olp ,my/daily-note-filename
                             ,my/daily-note-header
                             ("Meeting")))))
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n c" . org-roam-capture)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n I" . org-roam-node-insert-immediate)
         ("C-c n t" . my/org-roam-capture-task)
         ("C-c n k" . orb-insert-link)
         ("C-c n a" . orb-note-actions)
         ("C-c n d" . my/org-roam-jump-menu/body)
         ("C-c n P" . my/org-roam-insert-new-project)
         ("C-c n p" . my/org-roam-find-project)
         ("C-c n u" . org-roam-ui-mode)
         ("C-c n j" . org-roam-dailies-capture-today)
         :map org-mode-map
         ("C-M-i" . completion-at-point))
  :config
  (define-key org-roam-mode-map [mouse-1] (kbd "C-u <return>")) ; org-roam-buffer 界面左键相当于C-u <return>
  (setq org-roam-capture-templates  ; org-roam 笔记模板
        '(("d" "default" plain "- tag :: \n %?" ; 普及模板
           :target
           (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title} \n#+SETUPFILE: ./latex-preamble.org")
           :unnarrowed t)
          ("r" "bibliography reference in pdfs" plain ; 文献模板
           "#+FILETAGS: reading research \n - tags :: %^{keywords} \n* %^{title}
:PROPERTIES:\n:Custom_ID: %^{citekey}\n:URL: %^{url}\n:AUTHOR: %^{author-or-editor}\n:NOTER_DOCUMENT: ~/Nutstore/1/Nutstore/Zotero-Library/%^{citekey}.pdf\n:NOTER_PAGE:\n:END:"      
           :target
           (file+head "ref/${citekey}.org" "#+title: ${title}\n#+SETUPFILE: ../latex-preamble.org\n"))
          ("a" "article/post/blog/discussion" plain ; 其它阅读模板
           "#+FILETAGS: reading \n- tags :: \n- sources ::\n"      
           :target
           (file+head "article/%<%Y%m%d%H%M%S>-reading-${slug}.org" "#+title: ${title}\n#+SETUPFILE: ../latex-preamble.org\n"))
          ("s" "Seminar notes" plain "#+FILETAGS: seminar draft\n- title:\n- speaker:\n- event:\n- ref :: \n- tags ::" ; 学术报告模板
           :target
           (file+head "seminar/%<%Y%m%d>-seminar-${slug}.org" "#+title: ${title}\n#+SETUPFILE: ../latex-preamble.org"))))
  (require 'org-roam-dailies) 
  (org-roam-db-autosync-mode) ; 自动同步数据库
  (my/org-roam-refresh-agenda-list) ; 自动收集 project 文件中的待办事项
  (add-to-list 'org-after-todo-state-change-hook ; 将完成的待办事项备份至日记
               (lambda ()
                 (when (equal org-state "DONE")
                   (my/org-roam-copy-todo-to-today)))))

Org-roam-ui

org-roam-ui 自定义 LaTeX 宏命令

(defun my/set-ORUI-latex-macros ()
  (setq org-roam-ui-latex-macros
        '(("\\C" . "\\mathbb{C}")
          ("\\Fc" . "\\mathcal{F}")
          ("\\Nc" . "\\mathcal{N}")
          ("\\Ps" . "\\mathsf{P}")
          ("\\Pp" . "\\mathbf{P}")
          ("\\PP" . "\\mathbb{P}")
          ("\\E" . "\\mathsf{E}")
          ("\\Ee" . "\\mathbf{E}")
          ("\\EE" . "\\mathbb{E}")
          ("\\ONE" . "\\mathbf{1}")
          ("\\R" . "\\mathbb{R}")
          ("\\Z" . "\\mathbb{Z}")
          ("\\Q" . "\\mathbb{Q}")
          ("\\N" . "\\mathbb{N}")
          ("\\eps" . "\\varepsilon")
          ("\\det" . "\\mathop{det}"))))

org-roam-ui 可以显示节点连接图

(use-package org-roam-ui
  :straight
  (:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
  :after org-roam
  :custom
  (org-roam-ui-sync-theme t)
  (org-roam-ui-follow t)
  (org-roam-ui-update-on-save t)
  (org-roam-ui-open-on-start t)
  :config
  (my/set-ORUI-latex-macros))

文献阅读 (org-noter)

(use-package org-noter
  :straight (:host github
                   :repo "org-noter/org-noter"
                   :files ("*.el" "modules/*.el"))
  :bind
  (("C-c n n" . org-noter)
   :map org-noter-doc-mode-map
   ("M-e" . org-noter-insert-precise-note)
   ("e" . org-noter-insert-note)) ; This is incompatible with evil mode!
  :custom
  (org-noter-highlight-selected-text t)
  (org-noter-notes-search-path '("~/repos/notes/ref/"))
  (org-noter-auto-save-last-location t))

文献笔记设置

导入 Zotero 文献库, 并将文献笔记作为笔记节点

helm-bibtex

(use-package helm-bibtex
  :custom
  (bibtex-completion-notes-path org_notes)
  (bibtex-completion-bibliography zot_bib)
  (bibtex-completion-library-path zot_pdf))

org-roam-bibtex

(use-package org-roam-bibtex
  :after org-roam
  :hook (org-roam-mode . org-roam-bibtex-mode)
  :custom
  (orb-insert-interface 'helm-bibtex)
  (orb-insert-link-description 'citekey)
  (orb-preformat-keywords
   '("citekey" "title" "url" "author-or-editor" "keywords" "file"))
  (orb-process-file-keyword t)
  (orb-attached-file-extensions '("pdf")))

org-roam buffer 设置

(add-hook 'org-roam-mode-hook 'visual-line-mode) ; 自动换行

项目与待办管理

实现功能:

  • 定义一类特殊笔记类型: project
  • 新增/查找项目的函数
  • 在项目中插入待办并由 org-agenda 收集

基本函数与变量

(defvar my/org-roam-project-template ; 项目笔记模板
  '("p" "project" plain "** TODO %?"
    :if-new (file+head+olp "%<%Y%m%d%H>-${slug}.org"
                           "#+title: ${title}\n#+category: ${title}\n#+filetags: Project\n"
                           ("Tasks"))))
(defun my/org-roam-filter-by-tag (tag-name) ; 按 tag 搜索笔记; 需要 lexical binding
  (lambda (node)
    (member tag-name (org-roam-node-tags node)))) 
(defun my/org-roam-list-notes-by-tag (tag-name) ; 按 tag 显示笔记
  (mapcar #'org-roam-node-file
          (seq-filter
           (my/org-roam-filter-by-tag tag-name)
           (org-roam-node-list))))

新增/查找项目

  (defun my/org-roam-project-finalize-hook ()
  "Adds the captured project file to `org-agenda-files' if the
  capture was not aborted."
  ;; Remove the hook since it was added temporarily
  (remove-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)
  ;; Add project file to the agenda list if the capture was confirmed
  (unless org-note-abort
    (with-current-buffer (org-capture-get :buffer)
      (add-to-list 'org-agenda-files (buffer-file-name)))))

(defun my/org-roam-insert-new-project ()
  (interactive)
  ;; Add the project file to the agenda after capture is finished
  (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)
  ;; Select a project file to open, creating it if necessary
  (org-roam-capture- :node (org-roam-node-read
                            nil
                            (my/org-roam-filter-by-tag "Project"))
                     :templates (list my/org-roam-project-template)))

(defun my/org-roam-find-project ()
  (interactive)
  ;; Add the project file to the agenda after capture is finished
  (add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)
  ;; Select a project file to open, creating it if necessary
  (org-roam-node-find
   nil
   nil
   (my/org-roam-filter-by-tag "Project")))

把项目笔记加入 org-agenda 文件中

(defun my/org-roam-refresh-agenda-list ()
  (interactive)
  (setq org-agenda-files (my/org-roam-list-notes-by-tag "Project")))

新增项目中的待办

(defun my/org-roam-capture-task ()
(interactive)
;; 新增项目后, 更新 org-agende 文件列表
(add-hook 'org-capture-after-finalize-hook #'my/org-roam-project-finalize-hook)
;; 新增待办
(org-roam-capture- :node (org-roam-node-read
                          nil
                          (my/org-roam-filter-by-tag "Project"))
                   :templates (list my/org-roam-project-template)))

日记

月结/年终总结模板

(defun my/org-roam-goto-month ()
  (interactive)
  (org-roam-capture- :goto (when (org-roam-node-from-title-or-alias (format-time-string "%Y-%B")) '(4))
                     :node (org-roam-node-create)
                     :templates '(("m" "month" plain "\n* Goals\n\n%?* Summary\n\n"
                                   :if-new (file+head "%<%Y-%B>.org"
                                                      "#+title: %<%Y-%B>\n#+filetags: Project\n")
                                   :unnarrowed t))))

(defun my/org-roam-goto-year ()
  (interactive)
  (org-roam-capture- :goto (when (org-roam-node-from-title-or-alias (format-time-string "%Y")) '(4))
                     :node (org-roam-node-create)
                     :templates '(("y" "year" plain "\n* Goals\n\n%?* Summary\n\n"
                                   :if-new (file+head "%<%Y>.org"
                                                      "#+title: %<%Y>\n#+filetags: Project\n")
                                   :unnarrowed t))))

Hydra 定义的日记界面

(defhydra my/org-roam-jump-menu (:hint nil)
  "
^Dailies^        ^Capture^       ^Jump^
^^^^^^^^-------------------------------------------------
_t_: today       _T_: today       _m_: current month
_r_: tomorrow    _R_: tomorrow    _e_: current year
_y_: yesterday   _Y_: yesterday   ^ ^
_d_: date        ^ ^              ^ ^
"
  ("t" org-roam-dailies-goto-today)
  ("r" org-roam-dailies-goto-tomorrow)
  ("y" org-roam-dailies-goto-yesterday)
  ("d" org-roam-dailies-goto-date)
  ("T" org-roam-dailies-capture-today)
  ("R" org-roam-dailies-capture-tomorrow)
  ("Y" org-roam-dailies-capture-yesterday)
  ("m" my/org-roam-goto-month)
  ("e" my/org-roam-goto-year)
  ("c" nil "cancel"))

在日记中备份已完成待办

(defun my/org-roam-copy-todo-to-today ()
  (interactive)
  (unless (string= (buffer-name) "*habitica*") ; do nothing in habitica buffer
    (let ((org-refile-keep t) ; Set this to nil to delete the original!
          (org-roam-dailies-capture-templates
           '(("t" "tasks" entry "%?"
              :if-new (file+head+olp "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n" ("Tasks")))))
          (org-after-refile-insert-hook #'save-buffer)
          today-file
          pos)
      (save-window-excursion
        (org-roam-dailies--capture (current-time) t)
        (setq today-file (buffer-file-name))
        (setq pos (point)))
      ;; Only refile if the target file is different than the current file
      (unless (equal (file-truename today-file)
                     (file-truename (buffer-file-name)))
        (org-refile nil nil (list "Tasks" today-file nil pos))))))

即时加入空笔记的函数

(defun org-roam-node-insert-immediate (arg &rest args)
  (interactive "P")
  (let ((args (push arg args))
        (org-roam-capture-templates (list (append (car org-roam-capture-templates)
                                                  '(:immediate-finish t)))))
    (apply #'org-roam-node-insert args)))

项目管理

Magit

(use-package magit
  :commands magit-status
  :autoload  magit-get-current-branch
  :custom
  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1))

Perspective

(use-package perspective
  :defer 2
  :bind (("C-x C-b" . persp-list-buffers)
         ("C-x b" . persp-switch-to-buffer*)
         ("C-M-k" . persp-switch)
         ("C-M-n" . persp-next)
         ("C-x k" . persp-kill-buffer*))
  :custom
  (persp-mode-prefix-key (kbd "C-x x"))
  (persp-interactive-completion-function 'ido-completing-read)
  (persp-show-modestring 'header)
  :config
  (setq persp-sort 'created)
  (unless (equal persp-mode t) ; to avoid conflict with persp-mode
    (persp-mode))
  (setq persp-state-default-file (expand-file-name ".persp-save" user-emacs-directory)))

编程语言

终端

(use-package shell)

More colorful terminal

(use-package eterm-256color
:if (not my/is-termux)
:hook (term-mode . eterm-256color-mode))

字幕编辑

(use-package subed
  :straight
  (:host github :repo "sachac/subed" :files ("subed/*.el"))
  :config
  ;; Remember cursor position between sessions
  (add-hook 'subed-mode-hook 'save-place-local-mode)
  ;; Break lines automatically while typing
  (add-hook 'subed-mode-hook 'turn-on-auto-fill)
  ;; Break lines at 40 characters
  (add-hook 'subed-mode-hook (lambda () (setq-local fill-column 100)))
  ;; Some reasonable defaults
  (add-hook 'subed-mode-hook 'subed-enable-pause-while-typing)
  ;; As the player moves, update the point to show the current subtitle
  (add-hook 'subed-mode-hook 'subed-enable-sync-point-to-player)
  ;; As your point moves in Emacs, update the player to start at the current subtitle
  (add-hook 'subed-mode-hook 'subed-enable-sync-player-to-point)
  ;; Replay subtitles as you adjust their start or stop time with M-[, M-], M-{, or M-}
  (add-hook 'subed-mode-hook 'subed-enable-replay-adjusted-subtitle)
  ;; Loop over subtitles
  (add-hook 'subed-mode-hook 'subed-enable-loop-over-current-subtitle)
  ;; Show characters per second
  (add-hook 'subed-mode-hook 'subed-enable-show-cps))

python

(use-package python-mode)

cython

(use-package cython-mode
    :mode "\\.pyx\\'"
    :config
    (add-hook 'cython-mode-hook #'flycheck-mode))

elpy

follow suggestion from https://emacs.stackexchange.com/questions/10065/how-can-i-defer-loading-elpy-using-use-package to defer elpy mode

(use-package elpy
    :config
    (advice-add 'python-mode :before 'elpy-enable)
    (setq elpy-modules
          '(elpy-module-company elpy-module-eldoc elpy-module-flymake elpy-module-pyvenv elpy-module-highlight-indentation elpy-module-yasnippet elpy-module-django elpy-module-sane-defaults)))

yaml

(use-package yaml-mode
  :mode "\\.yml\\'")

info+

(use-package info+
  :defer 10)

Password management

(use-package auth-source-pass
  :config
  (auth-source-pass-enable))
(use-package pass)

启动时间优化

esup

(use-package esup
  :config
  (setq esup-depth 0))

显示启动时间

(defun efs/display-startup-time ()
    (message "Emacs loaded in %s with %d garbage collections."
             (format "%.2f seconds"
                     (float-time
                     (time-subtract after-init-time before-init-time)))
             gcs-done))
  (add-hook 'emacs-startup-hook #'efs/display-startup-time)

浏览器

eww 界面优化

(use-package shrface
  :after eww
  :config
  (shrface-basic)
  (shrface-trial)
  (shrface-default-keybindings) ; setup default keybindings
  (setq shrface-href-versatile t))

代码块

(use-package shr-tag-pre-highlight
  :after shr
  :config
  (add-to-list 'shr-external-rendering-functions
               '(pre . shr-tag-pre-highlight)))

EWW 设置

(use-package eww
  :custom
  (eww-retrieve-command nil)
  (eww-search-prefix "https://google.com/search?q=")
  (shr-use-fonts nil)
  :init
  (add-hook 'eww-after-render-hook #'shrface-mode)
  :config
  (add-hook 'eww-mode-hook 'visual-line-mode)
  (require 'shrface))

Mail

是否启动 mu4e

因为 mu4e 设置比较复杂, 这里直接用机器名称判断

(if my/is-termux
    (setq my/mu4e-dir "/data/data/com.termux/files/usr/share/emacs/site-lisp/mu4e/")
  (setq my/mu4e-dir "/usr/local/share/emacs/site-lisp/mu4e/"))
(setq my/mu4e-setting-file (expand-file-name "lisp/mu4e-setting-file.el" user-emacs-directory))
(setq my/mu4e-enabled (and (file-exists-p my/mu4e-dir)
                           (file-exists-p my/mu4e-setting-file)))

mu4e 设置

(when my/mu4e-enabled
  ;; fix for mu 1.10
  (defun mu4e--main-action-str (str &optional func-or-shortcut))
  (defun evil-collection-mu4e-update-main-view@override())
  (advice-add 'evil-collection-mu4e-update-main-view :override #'evil-collection-mu4e-update-main-view@override)
  (use-package mu4e
    :straight `(:local-repo ,my/mu4e-dir
                            :type nil :pre-build ())
    :defer 20 ; Wait until 20 seconds after startup
    :after (evil-collection auth-source-pass)
    :config

    ;; This is set to 't' to avoid mail syncing issues when using mbsync
    (setq mu4e-change-filenames-when-moving t)

    ;; Refresh mail using isync every 5 minutes
    (setq mu4e-update-interval (* 5 60))
    (setq mu4e-get-mail-command "mbsync -a")
    (setq mu4e-maildir "~/Mail")
    ;; header view customization
    (setq mu4e-view-fields '(:from-or-to :cc :subject :flags :date :maildir :mailing-list :tags :attachments :signature :decryption))
    ;; 设置 mu4e-context, bookmark, shortcut.
    (load-file my/mu4e-setting-file)))

auth-source-pass

Add password-store to auth-source

(use-package auth-source-pass
  :defer 2
  :config
  (auth-source-pass-enable))

博客

Hugo

(use-package ox-hugo
  :demand t
  :after ox
  :config
  (setq org-hugo-use-code-for-kbd t))

client 设置

(defun my/set-client-appearance-and-folding ()
  (interactive)
  (setq my/is-terminal (not window-system))
  (setq my/enable-folding window-system)
  (my/set-font-size)
  (my/set-font (my/get-font-height))
  (my/set-window)
  (my/set-line-number)
  (my/set-latex-font)
  (my/org-view-latex)
  (my/set-org-font)
  (setq org-hide-emphasis-markers nil))

(if (daemonp)
    (add-hook 'server-after-make-frame-hook #'my/set-client-appearance-and-folding)
  (my/set-client-appearance-and-folding))

WSL

(Taken from https://hungyi.net/posts/browse-emacs-urls-wsl/)

(when my/is-WSL
  (setq
   browse-url-generic-program  "/mnt/c/Windows/System32/cmd.exe"
   browse-url-generic-args     '("/c" "start")
   browse-url-browser-function #'browse-url-generic))

不常用/不稳定功能

弹出窗口管理 (popper)

Jinx

Habitica

(when (and my/is-turn-on-experimental
          (not my/is-android))
  (use-package habitica
    :defer 5
    :init
    (if (file-exists-p (setq my/habitica-credential-file (expand-file-name "habitica-credential.el" user-emacs-directory)))
        (load-file my/habitica-credential-file))
    :config
    ;; some partial evil keybindings for habitica
    (general-define-key
     :states 'normal
     :keymaps 'habitica-mode-map
     "gr" '(habitica-tasks :which-key "refresh habitica buffer"))
    (my/leader-def
      :keymaps 'org-mode-map
      "h" '(:ignore t :which-key "habitica command")
      "hh" '(my/create-habitica-todo-using-headline :which-key "current headline to hab task")
      "hf" '(my/habitica-finish-todo-in-org :which-key "finish a habitica todo")
      "hg" '(habitica-tasks :which-key "fetch habitica data")
      "hn" '(habitica-new-task :which-key "create a new task")
      "hu" '(habitica-up-task :which-key "up a habit/finish a task")
      "ht" '(my/habitica-finish-todo :which-key "finish a todo (fixed a bug)")
      "hx" '(habitica-delete-task :which-key "delete task")
      "hi" '(habitica-set-difficulty :which-key "set task difficulty")))
  )

自定义命令

完成任务

(defun my/habitica-finish-todo ()
  "Try to fix the login bug. Mark the current task as done or todo depending on its current state."
  (interactive)
  (if (not (habitica-buffer-p))
      (message "You must be inside the habitica buffer")
    (if (equal (format "%s" (org-element-property :todo-type (org-element-at-point))) "todo")
        (org-todo "DONE"))))

org-mode 中把当前标题发送至 habitica

(defun my/create-habitica-todo-using-headline (level)
  "Create a new todo based on current headline in a common org-file."
  (interactive "nEnter the difficulty level, 1 (easy) 2 (medium) 3 (hard): ")
  (let* ((headlines (nth 4 (org-heading-components)))
         (difficulty (format "%s" (car (nth (- level 1) habitica-difficulty))))
         (dif-string (format "%s" (cdr (nth (- level 1) habitica-difficulty)))))
    (message "habitica-task-name:%s" headlines)
    (habitica--update-properties (habitica-api-create-task "todo" headlines))
    (save-excursion
      (org-back-to-heading)
      (setq id (org-element-property :HABITICA_ID (org-element-at-point)))
      (habitica-api-set-difficulty id difficulty)
      (setq tags-str (org-make-tag-string (org-get-tags nil t)))
      (org-set-tags (format "%shab:%s:" tags-str dif-string))
      (org-todo "TODO"))))

org-mode 中完成当前 habitica 任务

(defun my/habitica-finish-todo-in-org ()
  (interactive)
  (if (equal (format "%s" (org-element-property :todo-type (org-element-at-point))) "todo")
      (progn (habitica-up-task)
             (if habitica-show-streak
                 (habitica--update-streak 1))
             (org-todo "DONE"))
    (message "This is not a habitica todo")))

结束代码

(setq gc-cons-threshold (* 2 1000 1000))
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Emacs Lisp
1
https://gitee.com/zhanlikuang/emacs-config.git
git@gitee.com:zhanlikuang/emacs-config.git
zhanlikuang
emacs-config
Emacs configuration
master

搜索帮助