From cfc5fc14afc6907fc07e6d186be91cdeae238e94 Mon Sep 17 00:00:00 2001 From: js0ny Date: Fri, 14 Mar 2025 14:55:41 +0000 Subject: [PATCH] feat(emacs): Better vanilla emacs --- tools/doom/evil.el | 4 +- tools/emacs.d/init.el | 2 +- tools/emacs.d/lisp/init-appearance.el | 7 +- tools/emacs.d/lisp/init-basic.el | 48 +++++++- tools/emacs.d/lisp/init-edit.el | 16 +++ tools/emacs.d/lisp/init-evil.el | 6 +- tools/emacs.d/lisp/init-file.el | 28 +++-- tools/emacs.d/lisp/init-keymaps.el | 2 +- tools/emacs.d/lisp/init-org.el | 161 ++++++++++++++++++-------- tools/emacs.d/lisp/init-package.el | 11 -- 10 files changed, 209 insertions(+), 76 deletions(-) diff --git a/tools/doom/evil.el b/tools/doom/evil.el index aec736e..b2614da 100644 --- a/tools/doom/evil.el +++ b/tools/doom/evil.el @@ -8,8 +8,8 @@ :nvom "n" 'evil-next-line :nvom "e" 'evil-previous-line :nvom "i" 'evil-forward-char - :nv "H" 'evil-prev-buffer - :nv "I" 'evil-next-buffer + :nvm "H" 'evil-prev-buffer + :nvm "I" 'evil-next-buffer :nvom "N" '(lambda () (interactive) (evil-next-line 5)) ; 5n :nvom "E" '(lambda () (interactive) (evil-previous-line 5)) ; 5e :nvom "C-w n" 'evil-window-down diff --git a/tools/emacs.d/init.el b/tools/emacs.d/init.el index d914c48..636e55c 100644 --- a/tools/emacs.d/init.el +++ b/tools/emacs.d/init.el @@ -14,7 +14,7 @@ (add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory)) ;; Store the auto-generated custom config to `custom.el` -(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) +(setq custom-file (expand-file-name ".custom.el" user-emacs-directory)) ;; Load each modules (require 'init-package) ; package manager should be loaded first diff --git a/tools/emacs.d/lisp/init-appearance.el b/tools/emacs.d/lisp/init-appearance.el index 958326b..21aeeca 100644 --- a/tools/emacs.d/lisp/init-appearance.el +++ b/tools/emacs.d/lisp/init-appearance.el @@ -1,12 +1,11 @@ ;;; init-appearance.el -;; Set Font - JetBrains Mono Nerd Font (when (display-graphic-p) - (add-to-list 'default-frame-alist '(font . "JetBrainsMono NF"))) + (add-to-list 'default-frame-alist '(font . "Sarasa Mono SC"))) -;; Set CJK Display Font - LXGW Wenkai Mono +;; Set CJK Display Font (dolist (charset '(kana han cjk-misc bopomofo)) - (set-fontset-font t charset (font-spec :family "霞鹜文楷等宽"))) + (set-fontset-font t charset (font-spec :family "Sarasa Mono SC"))) ;; Icon Support ;; Once installed, Manually install the fonts required: diff --git a/tools/emacs.d/lisp/init-basic.el b/tools/emacs.d/lisp/init-basic.el index 7f6a94a..4ad405e 100644 --- a/tools/emacs.d/lisp/init-basic.el +++ b/tools/emacs.d/lisp/init-basic.el @@ -14,10 +14,20 @@ :hook (prog-mode . display-line-numbers-mode)) +(defvar xdg-data-home + (or (getenv "XDG_DATA_HOME") + (expand-file-name "~/.local/share"))) + +(defvar xdg-cache-home + (or (getenv "XDG_CACHE_HOME") + (expand-file-name "~/.local/cache"))) + +(defvar user-emacs-data (expand-file-name "emacs" xdg-data-home)) +(defvar user-emacs-cache (expand-file-name "emacs" xdg-cache-home)) ;; https://book.emacs-china.org/#orgcfd105e Open with Emacs -(server-mode 1) +;; (server-mode 1) (require 'recentf) @@ -33,5 +43,41 @@ :config (setq alert-default-style 'libnotify)) +;; 设置 Emacs 备份和自动保存目录 +(defvar user-backup-directory (expand-file-name "backups" user-emacs-data)) +(defvar user-autosaves-directory (expand-file-name "autosaves" user-emacs-cache)) + +;; 创建目录(如果不存在) +(dolist (dir (list user-backup-directory user-autosaves-directory)) + (unless (file-exists-p dir) + (make-directory dir t) + (message "Creating directory: %s" dir))) + +;; 配置备份文件目录 +(setq backup-directory-alist `(("." . ,user-backup-directory))) + +(when (boundp 'project-list-file) + (setq project-list-file (expand-file-name "projects-list" user-emacs-data))) + +;; 配置自动保存文件目录 +(setq auto-save-file-name-transforms + `((".*" ,user-autosaves-directory t))) + +;; 配置自动保存列表文件前缀 +(setq auto-save-list-file-prefix + (expand-file-name "auto-save-list/.saves-" user-autosaves-directory)) + +;; TRAMP 远程文件的备份设置 +(setq tramp-backup-directory-alist (copy-tree backup-directory-alist)) + + +;; 备份设置(可选) +(setq backup-by-copying t ; 使用复制而非重命名 + delete-old-versions t ; 自动删除旧版本 + kept-new-versions 6 ; 保留的新版本数量 + kept-old-versions 2 ; 保留的旧版本数量 + version-control t) ; 使用版本号 + + ;; Export module (provide 'init-basic) diff --git a/tools/emacs.d/lisp/init-edit.el b/tools/emacs.d/lisp/init-edit.el index 85b7d6c..519304f 100644 --- a/tools/emacs.d/lisp/init-edit.el +++ b/tools/emacs.d/lisp/init-edit.el @@ -60,9 +60,25 @@ ;; Avy - act like Vim Easymotion (use-package avy + :ensure t :config (evil-define-key '(normal) 'global (kbd "T") 'avy-goto-char) (evil-define-key '(normal) 'global (kbd "s") 'avy-goto-char-2) (evil-define-key '(normal) 'global (kbd "s") 'avy-goto-char-2-above)) +(use-package yasnippet + :ensure t + :defer t + :commands yas-minor-mode + :hook ((prog-mode . yas-minor-mode) + (org-mode . yas-minor-mode)) + :config + (setq yas-indent-line 'fixed) ;; 让 snippet 展开时不影响缩进 + (setq yas-wrap-around-region nil) ;; 关闭自动包裹选中区域 + (advice-add 'yas-expand-snippet :around + (lambda (orig-fn &rest args) + (let ((inhibit-field-text-motion t)) ;; 禁止字段自动换行 + (apply orig-fn args)))) + (yas-reload-all)) + (provide 'init-edit) diff --git a/tools/emacs.d/lisp/init-evil.el b/tools/emacs.d/lisp/init-evil.el index 5badba3..52845ce 100644 --- a/tools/emacs.d/lisp/init-evil.el +++ b/tools/emacs.d/lisp/init-evil.el @@ -38,7 +38,8 @@ (global-evil-leader-mode) (evil-leader/set-leader "") (evil-leader/set-key - "b" 'buffer-menu)) + "b" 'buffer-menu + "ft" 'treemacs)) ;; Evil Commentary: Use gc to toggle comments (use-package evil-commentary @@ -65,4 +66,7 @@ ;; other faces such as `diff-added` will be used for other actions (evil-goggles-use-diff-faces)) +(with-eval-after-load 'evil + (evil-set-initial-state 'org-agenda-mode 'motion)) + (provide 'init-evil) diff --git a/tools/emacs.d/lisp/init-file.el b/tools/emacs.d/lisp/init-file.el index 222f9f4..d49785c 100644 --- a/tools/emacs.d/lisp/init-file.el +++ b/tools/emacs.d/lisp/init-file.el @@ -37,9 +37,6 @@ ("C-x t C-t" . treemacs-find-file) ("C-x t M-t" . treemacs-find-tag) ) - :config - (evil-leader/set-key - "ft" 'treemacs) ) (use-package treemacs-evil @@ -47,12 +44,25 @@ :ensure t :config ;; Evil treemacs state bindings - (define-key evil-treemacs-state-map (kbd "n") #'treemacs-next-line) - (define-key evil-treemacs-state-map (kbd "e") #'treemacs-previous-line) - (define-key evil-treemacs-state-map (kbd "N") #'treemacs-next-neighbour) - (define-key evil-treemacs-state-map (kbd "E") #'treemacs-previous-neighbour) - (define-key evil-treemacs-state-map (kbd "H") #'treemacs-toggle-show-dotfiles) - (define-key evil-treemacs-state-map (kbd "I") #'treemacs-hide-gitignored-files-mode) + (evil-define-key evil-treemacs-state-map + "n" #'treemacs-next-line + "e" #'treemacs-previous-line + "N" #'treemacs-next-neighbour + "E" #'treemacs-previous-neighbour + "H" #'treemacs-toggle-show-dotfiles + "I" #'treemacs-hide-gitignored-files-mode + "i" #'treemacs-RET-action + "a" #'treemacs-create-file + "A" #'treemacs-create-dir + "c" #'treemacs-copy-file + "x" #'treemacs-move-file + "d" #'treemacs-delete-file + "r" #'treemacs-rename-file + "q" #'treemacs-quit + "y" #'treemacs-copy-relative-path-at-point + "Y" #'treemacs-copy-absolute-path-at-point + "m" #'treemacs-mark-or-unmark-path-at-point) + ;; Treemacs mode specific binding (evil-define-key 'treemacs treemacs-mode-map (kbd "i") #'treemacs-RET-action)) diff --git a/tools/emacs.d/lisp/init-keymaps.el b/tools/emacs.d/lisp/init-keymaps.el index f0c2ebf..ae4081f 100644 --- a/tools/emacs.d/lisp/init-keymaps.el +++ b/tools/emacs.d/lisp/init-keymaps.el @@ -3,6 +3,6 @@ ;; Once this is defined, `M-x open-init-file` will open this file (defun open-org-config() (interactive) ;; Mark function as `interactive` that allows user to access by M-x open-init-file RET - (find-file "~/.emacs.d/lisp/init-org.el")) + (find-file "~/.dotfiles/tools/emacs.d/lisp/init-org.el")) (provide 'init-keymaps) diff --git a/tools/emacs.d/lisp/init-org.el b/tools/emacs.d/lisp/init-org.el index 4fb49ed..0f332d1 100644 --- a/tools/emacs.d/lisp/init-org.el +++ b/tools/emacs.d/lisp/init-org.el @@ -3,22 +3,24 @@ ;; Use =M-x org-mode-restart= to take effects ;; After =M-x eval-buffer= +;; Org General + (use-package org :ensure nil ;; Use Emacs built-in Org Mode :config (setq org-log-done 'time) - (setq org-startup-indented t) + (setq org-startup-indented nil) ;; Use inline image (setq org-startup-with-inline-images t) (setq org-display-remote-inline-images 'cache) ; 预览网络图片 ;; Use LaTeX rendering - (setq org-startup-with-latex-preview t) + (setq org-startup-with-latex-preview nil) (setq org-image-actual-width 600) ;; Conceal on markup markers (setq org-hide-emphasis-markers t) - (setq org-directory "~/OrgFiles") - (setq org-agenda-files '("~/OrgFiles")) + (setq org-directory "~/OrgFiles/") + (setq org-agenda-files (list (concat org-directory "tasks/"))) (setq org-pretty-entities t) (setq org-src-fontify-natively t) (setq org-src-tab-acts-natively t) ; Use TAB to indent inside source block @@ -29,6 +31,7 @@ (evil-define-key 'normal org-mode-map (kbd "SPC a") 'org-agenda-list) ; TODO: Here Simulates the leader ) +;; Org Styling ;; Set Org Styles (setq org-emphasis-alist @@ -40,33 +43,18 @@ ("+" (:strike-through t)))) +;; Org Preview + ;; Better LaTeX preview + +(setq org-latex-preview-ltxpng-directory (expand-file-name "emacs/org/latex" xdg-cache-home)) +(setq org-preview-latex-default-process 'dvisvgm) +(setq org-format-latex-options (plist-put org-format-latex-options :scale 0.8)) + (use-package org-fragtog :after org :hook (org-mode . org-fragtog-mode)) -;; Run source block with C-c C-c -;; Add supports for non-elisp langs -(use-package ob-python - :ensure nil ; Part of Org Mode - :after org) - -(use-package ob-C ;; C++ support is integrated in ob-C - :ensure nil ; Part of Org Mode - :after org) - -(org-babel-do-load-languages - 'org-babel-load-languages - '( - (python . t) ; No need to declare C/C++/emacs-lisp since is integrated - (shell . t) - )) -;; Use clang as default C/C++ Compiler (Easier to install and configure in Windows) -(setq org-babel-C-compiler "clang") -(setq org-babel-C++-compiler "clang++") -;; Don't ask me again when running source block -(setq org-confirm-babel-evaluate nil) - ;; Pasting Images ;; Use =M-x org-download-clipboard= to paste image ;; Additional program required: magick @@ -105,26 +93,107 @@ ;; Pomodoro for org clock (use-package org-pomodoro) -;; Org Styles Integration (use-package org-modern :ensure t - :after (org) :config - (with-eval-after-load 'org (global-org-modern-mode)) -;; https://github.com/minad/org-modern/issues/232 - (setq org-modern-fold-stars - '(("▶" . "▼") - ("▷" . "▽") - ;; ("⯈" . "⯆") - ("▷" . "▽") - ("▹" . "▿") - ("▸" . "▾")) -) -) + (setopt org-modern-star 'replace + org-modern-replace-stars '("§") + org-modern-hide-stars "§") + (setopt org-modern-list '((?- . "•"))) + (setopt org-modern-timestamp '(" %Y-%m-%d " . " %H:%M ")) + (setopt org-modern-block-fringe nil) + +;; https://github.com/neoheartbeats/.emacs.d/blob/main/lisp/init-org.el#L126C1-L159C47 + (defun sthenno/org-modern-spacing () + "Adjust line-spacing for `org-modern' to correct svg display." + + ;; FIXME: This may not set properly + (setq-local line-spacing (cond ((eq major-mode #'org-mode) 0.20) + (t nil)))) + (add-hook 'org-mode-hook #'sthenno/org-modern-spacing) + + + ;; Hooks + (add-hook 'org-mode-hook #'org-modern-mode)) + +;; WARNING: This package will cause infinite recursion when initialising org +;; Test on 2025-03-14 Emacs 30 and Arch Linux +;(use-package mixed-pitch +; :hook +; (text-mode . org-mode)) + +;; Org Agenda + +(with-eval-after-load 'org-agenda + (evil-define-key 'motion org-agenda-mode-map + (kbd "n") #'org-agenda-next-line + (kbd "e") #'org-agenda-previous-line + (kbd "gn") #'org-agenda-next-item + (kbd "ge") #'org-agenda-previous-item + (kbd "N") #'org-agenda-priority-up + (kbd "E") #'org-agenda-priority-down + (kbd "i") #'evil-forward-char + (kbd ",") #'org-agenda-priority + (kbd "x") #'org-agenda-todo + (kbd "t") #'org-agenda-set-tags + (kbd "w") #'org-save-all-org-buffers + (kbd "ds") #'org-agenda-schedule + (kbd "dd") #'org-agenda-deadline + (kbd "$") #'org-agenda-archive + (kbd "!") #'org-agenda-toggle-deadlines + (kbd "cp") #'org-pomodoro + (kbd "vd") #'org-agenda-day-view + (kbd "vw") #'org-agenda-week-view + (kbd "vm") #'org-agenda-month-view + (kbd "vy") #'org-agenda-year-view + (kbd "v.") #'org-agenda-reset-view)) + +(evil-leader/set-key "A" #'org-agenda) + +(setq org-agenda-prefix-format '((agenda . " %i %-12:c%?-12t% s") + (todo . " %i %-12:c %e") + (tags . " %i %-12:c %e ") + (search . " %i %-12:c %e "))) + + +;; Org Babel + +;; Run source block with C-c C-c +;; Add supports for non-elisp langs +(use-package ob-python + :ensure nil ; Part of Org Mode + :after org) + +(use-package ob-C ;; C++ support is integrated in ob-C + :ensure nil ; Part of Org Mode + :after org) + +(org-babel-do-load-languages + 'org-babel-load-languages + '( + (python . t) ; No need to declare C/C++/emacs-lisp since is integrated + (shell . t) + )) +;; Don't ask me again when running source block +;; Use clang as default C/C++ Compiler on macOS and Windows +(setq org-confirm-babel-evaluate nil) +(if (not (eq system-type 'gnu/linux)) + (setq org-babel-C-compiler "clang")) + +(setq org-babel-default-header-args:C + '((:includes . " "))) + +(setq org-babel-default-header-args:C++ + '((:includes . " "))) + +;; Org Export + +;; icalendar + +(setq org-icalendar-use-scheduled '(event-if-todo event-if-not-todo)) +(setq org-icalendar-use-deadline '(event-if-todo event-if-not-todo)) +(setq org-icalendar-combined-agenda-file "~/Dropbox/org.ics") -(use-package mixed-pitch - :hook - (text-mode . org-mode)) ;; Integrate Emacs Timer with System Notifications ;; 定义通知函数 @@ -140,12 +209,12 @@ "检查当前正在运行的 clock 是否到期" (when (org-clocking-p) (let* ((clocked-time (org-clock-get-clocked-time)) - (effort (org-duration-to-minutes + (effort (org-duration-to-minutes (or (org-entry-get (org-clock-is-active) "Effort") "0"))) (remaining (- effort clocked-time))) (when (and (> effort 0) (<= remaining 0)) - (my/org-clock-notification + (my/org-clock-notification "Org Clock 提醒" (format "任务 '%s' 的预计时间已到!" (org-clock-get-clock-string))))))) @@ -158,9 +227,9 @@ (lambda () (let ((effort (org-entry-get (point) "Effort"))) (when effort - (my/org-clock-notification + (my/org-clock-notification "开始计时" - (format "开始计时任务: %s\n预计用时: %s" + (format "开始计时任务: %s\n预计用时: %s" (org-get-heading t t t t) effort)))))) diff --git a/tools/emacs.d/lisp/init-package.el b/tools/emacs.d/lisp/init-package.el index 0e6ea11..2203dd7 100644 --- a/tools/emacs.d/lisp/init-package.el +++ b/tools/emacs.d/lisp/init-package.el @@ -66,17 +66,6 @@ ;; Use Emacs mode to use number to navigate dashboard ) -;(use-package dashboard -; :ensure t -; :init -; (dashboard-setup-startup-hook) -; :config -; (setq dashboard-banner-logo-title "EMACS") -; (setq dashboard-startup-banner 'official) -; (setq dashboard-items '((recents . 5) -; (bookmark . 5)))) -; - (use-package wakatime-mode :ensure t