diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 05b40d9..d1e210e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,3 +24,9 @@ jobs: - name: build emacs-x run: bash build.sh + + - name: Release + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: emacs-x-*-installer.* diff --git a/README.org b/README.org index 8300340..28c9f08 100644 --- a/README.org +++ b/README.org @@ -11,7 +11,7 @@ 1. 下载配置文件 #+begin_src shell - git clone https://github.com/xhcoding/.emacs.d.git ~/.emacs.d + git clone https://github.com/xhcoding/emacs.d.git ~/.emacs.d #+end_src 2. 运行 =org-tangle.bat= 生成 =.el= 文件 @@ -20,6 +20,8 @@ * 目录 :TOC: - [[#安装方法][安装方法]] - [[#early-initel][early-init.el]] + - [[#设置配置启动模式][设置配置启动模式]] + - [[#开启-debug-on-error][开启 debug-on-error]] - [[#将-gc-的阈值设置到最大避免启动的时候多次-gc-拖慢启动速度][将 gc 的阈值设置到最大,避免启动的时候多次 gc 拖慢启动速度]] - [[#file-handler-临时设置为-nil加快启动速度][file handler 临时设置为 nil,加快启动速度]] - [[#不要初始化-package][不要初始化 package]] @@ -50,12 +52,11 @@ - [[#yes-or-no-p-改为-y-or-n-p][yes-or-no-p 改为 y-or-n-p]] - [[#关掉提示声音][关掉提示声音]] - [[#禁止光标闪烁][禁止光标闪烁]] - - [[#启动时不要显示-for-information-about-gnu-emacs][启动时不要显示 For information about GNU Emacs...]] + - [[#启动时不要显示][启动时不要显示]] - [[#tab-设置][tab 设置]] - [[#一行不超过-120-字符][一行不超过 120 字符]] - [[#删除文件时放到回收站][删除文件时放到回收站]] - [[#高亮当前行][高亮当前行]] - - [[#利用左边-win-键][利用左边 Win 键]] - [[#gcmh-优化垃圾回收][gcmh 优化垃圾回收]] - [[#auto-save-自动保存][auto-save 自动保存]] - [[#rime-智能的中文输入法][rime 智能的中文输入法]] @@ -73,7 +74,9 @@ - [[#ace-window-快速切换到其它-window][ace-window 快速切换到其它 window]] - [[#toggle-one-window-快速切换到单窗口][toggle-one-window 快速切换到单窗口]] - [[#yasnippet-快速插入代码片段][yasnippet 快速插入代码片段]] - - [[#projectel-工程管理][project.el 工程管理]] + - [[#separedit-快速编辑][separedit 快速编辑]] + - [[#projectileel-工程管理][projectile.el 工程管理]] + - [[#compile-增强][compile 增强]] - [[#eshell-增强][eshell 增强]] - [[#eshell-基本配置][eshell 基本配置]] - [[#eshell-历史记录设置][eshell 历史记录设置]] @@ -86,6 +89,7 @@ - [[#highlight-parentheses-高亮外层括号][highlight-parentheses 高亮外层括号]] - [[#evil-nerd-commenter-智能注释代码][evil-nerd-commenter 智能注释代码]] - [[#apheleia-智能格式化代码][apheleia 智能格式化代码]] + - [[#wuco-拼写检查][wuco 拼写检查]] - [[#git-配置][git 配置]] - [[#magit][magit]] - [[#显示当前行的最后提交信息][显示当前行的最后提交信息]] @@ -111,8 +115,9 @@ - [[#css][css]] - [[#json-ts-mode][json-ts-mode]] - [[#qml-开发][qml 开发]] + - [[#lua-开发][lua 开发]] - [[#plantuml-支持][plantuml 支持]] - - [[#dap-mode-调试][dap-mode 调试]] + - [[#dape-调试][dape 调试]] - [[#dash-docs-查询-dash-文档][dash-docs 查询 dash 文档]] - [[#shrface-让-eww-的阅读体验更好][shrface 让 eww 的阅读体验更好]] - [[#eww-配置][eww 配置]] @@ -127,6 +132,7 @@ - [[#org-contrib-wanderlust-支持][org-contrib wanderlust 支持]] - [[#增量载入-org-包][增量载入 org 包]] - [[#知识管理及博客配置][知识管理及博客配置]] + - [[#denote][denote]] - [[#org-roam][org-roam]] - [[#hugo-配置][hugo 配置]] - [[#上传博客图片到腾讯云对象存储][上传博客图片到腾讯云对象存储]] @@ -145,7 +151,7 @@ - [[#which-key-按键提示][which-key 按键提示]] - [[#meow-模式编辑][meow 模式编辑]] - [[#启动-emacs-server][启动 emacs server]] - - [[#daemon-模式下启动更多内容][daemon 模式下启动更多内容]] + - [[#载入私有配置文件][载入私有配置文件]] * early-init.el :PROPERTIES: @@ -158,11 +164,35 @@ Emacs 启动过程的早期加载。 ;;; early-init.el --- early init -*- lexical-binding: t no-byte-compile: t; -*- #+end_src +** 设置配置启动模式 + +#+begin_src elisp + (defconst my-config-mode (intern (downcase (or (getenv "EMACS_CONFIG_MODE") "full")))) + + (defconst full? (eq my-config-mode 'full)) + + (defconst minimal? (eq my-config-mode 'minimal)) + + (defconst debug? (eq my-config-mode 'debug)) + + (defconst not-full? (not full?)) + + (defconst not-minimal? (not minimal?)) + + (defconst not-debug? (not debug?)) +#+end_src + +** 开启 debug-on-error + +#+begin_src elisp + (when debug? + (setq toggle-debug-on-error t)) +#+end_src + ** 将 gc 的阈值设置到最大,避免启动的时候多次 gc 拖慢启动速度 #+begin_src elisp (setq gc-cons-threshold most-positive-fixnum) - (setq debug-on-error t) #+end_src ** file handler 临时设置为 nil,加快启动速度 @@ -213,23 +243,26 @@ Emacs 启动过程的早期加载。 ** 启动后全屏 #+begin_src elisp - (push '(fullscreen . fullscreen) default-frame-alist) + (when full? + (push '(fullscreen . fullscreen) default-frame-alist)) + #+end_src ** straight 包管理 #+begin_src elisp - (defvar bootstrap-version) (let ((bootstrap-file - (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) - (bootstrap-version 6)) + (expand-file-name + "straight/repos/straight.el/bootstrap.el" + (or (bound-and-true-p straight-base-dir) + user-emacs-directory))) + (bootstrap-version 7)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el" 'silent 'inhibit-cookies) - (message "%s" (buffer-string)) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) @@ -331,12 +364,13 @@ from: https://github.com/doomemacs/doomemacs/blob/master/lisp/doom-start.el ** 启动速度测试 #+begin_src elisp -(use-package benchmark-init - :straight t - :demand t - :hook (after-init . benchmark-init/deactivate) - :bind ("" . benchmark-init/show-durations-tree) - ) + (use-package benchmark-init + :if debug? + :straight t + :demand t + :hook (after-init . benchmark-init/deactivate) + :bind ("" . benchmark-init/show-durations-tree) + ) #+end_src @@ -348,3 +382,2986 @@ from: https://github.com/doomemacs/doomemacs/blob/master/lisp/doom-start.el #+begin_src elisp ;;; init.el --- init -*- lexical-binding: t no-byte-compile: t; -*- #+end_src + +** 载入 custom 文件 + +#+begin_src elisp + (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) + (load custom-file t) +#+end_src + +** 定义一些固定的常量 +#+begin_src elisp + (defconst sys-is-windows (memq system-type '(cygwin windows-nt ms-dos))) + + (defconst sys-no-windows (not sys-is-windows)) + + ;; 动态库目录 + (defconst my-lib-dir (expand-file-name "lib" user-emacs-directory)) + (add-to-list 'load-path my-lib-dir) + + ;; 可执行文件目录 + (defconst my-bin-dir (expand-file-name "bin" user-emacs-directory)) + ;; 将 my-bin-dir 加入到 PATH 中 + (setenv "PATH" (concat my-bin-dir (if sys-is-windows ";" ":") (getenv "PATH"))) + + + ;; 外部配置文件目录 + (defconst my-etc-dir (expand-file-name "etc" user-emacs-directory)) + + ;; 存放 org 文件的目录 + (defconst my-org-dir (expand-file-name "~/Org/")) + + ;; 私有文件目录 + (defconst my-private-dir (expand-file-name "private" my-org-dir)) + + ;; 私有代码片段目录 + (defconst my-private-snippets-dir (expand-file-name "snippets" my-private-dir)) + + ;; 存放代码的目录 + (if sys-is-windows + (defconst my-code-dir (expand-file-name "D:/Code")) + (defconst my-code-dir (expand-file-name "~/Code"))) + + ;; 存放一些存档文件的目录 + (if sys-is-windows + (defconst my-archives-dir (expand-file-name "D:/Archives")) + (defconst my-archives-dir (expand-file-name "~/Archives"))) + +#+end_src + +** 常用的工具函数 + +*** 重命名当前文件 + +#+begin_src elisp + (defun my/rename-this-file-and-buffer (new-name) + "Rename both current buffer and file it's visiting to NEW_NAME." + (interactive "sNew name: ") + (let ((name (buffer-name)) + (filename (buffer-file-name))) + (unless filename + (error "Buffer '%s' is not visiting a file" name)) + (progn + (when (file-exists-p filename) + (rename-file filename new-name 1)) + (set-visited-file-name new-name) + (rename-buffer new-name)))) +#+end_src + +*** 删除当前文件 + +#+begin_src emacs-lisp + (defun my/delete-file-and-buffer () + "Kill the current buffer and deletes the file it is visiting." + (interactive) + (let ((filename (buffer-file-name))) + (when (and filename (y-or-n-p (concat "Do you really want to delete file " filename "?"))) + (delete-file filename t) + (message "Deleted file %s." filename) + (kill-buffer) + ))) +#+end_src + +*** 清理 Message buffer + +#+begin_src elisp + (defun my/clear-messages-buffer () + "Clear *Messages* buffer." + (interactive) + (let ((inhibit-read-only t)) + (with-current-buffer "*Messages*" + (erase-buffer)))) +#+end_src + +*** 开关网络代理 + +#+begin_src elisp + (defun my/toggle-url-proxy () + "Toggle proxy for the url.el library." + (interactive) + (require 'url) + (cond + (url-proxy-services + (message "Turn off URL proxy") + (setq url-proxy-services nil)) + (t + (message "Turn on URL proxy") + (setq url-proxy-services + '(("http" . "127.0.0.1:7890") + ("https" . "127.0.0.1:7890") + ("no_proxy" . "0.0.0.0")))))) + + +#+end_src + +*** 清理掉没用的 buffer + +#+begin_src elisp + (defun my/kill-unused-buffers () + "Kill unused buffers." + (interactive) + (ignore-errors + (save-excursion + (dolist (buf (buffer-list)) + (set-buffer buf) + (when (and (string-prefix-p "*" (buffer-name)) (string-suffix-p "*" (buffer-name))) + (kill-buffer buf)) + )))) +#+end_src + +** 基本设置 +*** 用户信息 + +#+begin_src elisp + (setq user-full-name "xhcoding" + user-mail-address "xhcoding@foxmail.com") +#+end_src + +*** 增加 IO 性能 + +#+begin_src elisp + (setq process-adaptive-read-buffering nil) + (setq read-process-output-max (* 1024 1024)) +#+end_src + +*** 设置编码 + +默认使用 utf-8 ,在 windows 文件名编码使用 gbk ,不然打不开中文文件 +#+begin_src elisp + (prefer-coding-system 'utf-8) + (when sys-is-windows + (setq file-name-coding-system 'gbk)) +#+end_src + +*** 设置 frame 标题 + +#+begin_src emacs-lisp + (setq frame-title-format "Emacs") +#+end_src + +*** 关闭启动动画 + +#+begin_src elisp + (setq inhibit-startup-message t) +#+end_src + +*** scratch buffer 设置 + +scratch 为 fundaemental-mode + +#+begin_src elisp +(setq initial-major-mode 'fundamental-mode) +#+end_src + +scratch buffer 内容为空 + +#+begin_src elisp + (setq initial-scratch-message nil) +#+end_src + +*** yes-or-no-p 改为 y-or-n-p + +#+begin_src elisp + (setq use-short-answers t) +#+end_src + +*** 关掉提示声音 + +#+begin_src elisp + (setq visible-bell t) +#+end_src + +*** 禁止光标闪烁 + +#+begin_src elisp + (blink-cursor-mode -1) +#+end_src + +*** 启动时不要显示 +For information about GNU Emacs... + +#+begin_src elisp + (advice-add #'display-startup-echo-area-message :override #'ignore) +#+end_src + +*** tab 设置 + +#+begin_src elisp + (setq-default tab-width 4) + + (setq-default indent-tabs-mode nil) +#+end_src + +*** 一行不超过 120 字符 + +#+begin_src elisp + (setq-default fill-column 120) + (column-number-mode +1) +#+end_src + +*** 删除文件时放到回收站 + +#+begin_src elisp +(setq-default delete-by-moving-to-trash t) +#+end_src + +*** 高亮当前行 + +#+begin_src elisp +(global-hl-line-mode +1) +#+end_src + +** gcmh 优化垃圾回收 + +#+begin_src elisp + (use-package gcmh + :straight t + :hook (emacs-startup . gcmh-mode) + :init + (setq gcmh-idle-delay 'auto + gcmh-auto-idle-delay-factor 10 + gcmh-high-cons-threshold 33554432)) ; 32MB +#+end_src + +** auto-save 自动保存 +ref: https://github.com/manateelazycat/auto-save + +#+begin_src elisp + (use-package auto-save + :straight (auto-save :type git :host github :repo "manateelazycat/auto-save") + :defer 3 + :init + ;; 关闭 emacs 默认的自动备份 + (setq make-backup-files nil) + ;; 关闭 emacs 默认的 自动保存 + (setq auto-save-default nil) + :config + (setq auto-save-silent t) + (auto-save-enable) + ) + + +#+end_src + +** rime 智能的中文输入法 + +#+begin_src elisp + (use-package rime + :straight t + :defer t + :bind ("C-j" . rime-force-enable) + :init + (setq rime--module-path + (expand-file-name (concat "librime-emacs" module-file-suffix) my-lib-dir)) + (when sys-is-windows + (setq rime-share-data-dir (expand-file-name "rime-data" my-etc-dir))) + + (defun my-*require-rime(&rest _) + "Require rime when toggle-input-method." + (unless (featurep 'rime) + (require 'rime))) + + (advice-add 'toggle-input-method :before #'my-*require-rime) + + :custom + (default-input-method "rime") + (rime-show-candidate 'posframe) + (rime-user-data-dir (expand-file-name "rime-user" my-etc-dir)) + + :config + (setq + rime-disable-predicates '(rime-predicate-after-alphabet-char-p + rime-predicate-prog-in-code-p))) + +#+end_src + +** 字体和主题 + +#+begin_src elisp + (use-package faces + :if full? + :custom-face + (variable-pitch + ((t (:font ,(font-xlfd-name (font-spec :family "Alegreya")))))) + (default + ((t (:font ,(font-xlfd-name (font-spec :family "CaskaydiaCove NFP")) + :height 120)))) + (fixed-pitch + ((t (:inherit default)))) + :config + (set-fontset-font t 'emoji (font-spec :family "Segoe UI Symbol")) + + (set-fontset-font t 'chinese-gbk (font-spec :family "微软雅黑")) + + ) + + + (use-package ef-themes + :if full? + :straight t + :config + (load-theme 'ef-summer :no-confirm) + ) +#+end_src + +** ligature 连字体 + +#+begin_src elisp + (use-package ligature + :if full? + :straight t + :defer t + :hook prog-mode + :config + ;; Enable all Cascadia Code ligatures in programming modes + (ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "" "---" "-<<" + "<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->" + "<--" "<-<" "<<=" "<<-" "<<<" "<+>" "" "###" "#_(" "..<" + "..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~=" + "~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|" + "[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:" + ">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:" + "<$" "<=" "<>" "<-" "<<" "<+" "" "++" "?:" + "?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)" + "\\\\" "://"))) +#+end_src + +** awesome-tray 底部信息显示 + +#+begin_src elisp + (use-package awesome-tray + :demand t + :straight (awesome-tray :type git :host github :repo "manateelazycat/awesome-tray") + :custom + (awesome-tray-mode-line-active-color "#94d4ff") + (awesome-tray-mode-line-inactive-color "#9f9f9f") + (awesome-tray-date-format "%H:%M") + (awesome-tray-git-format "%s") + (awesome-tray-buffer-name-max-length 40) + (awesome-tray-active-modules '("location" "buffer-name" "buffer-encoding" "input-method" "date")) + :config + + (defun my-awesome-tray-module-buffer-encoding-info () + (concat (pcase (coding-system-eol-type buffer-file-coding-system) + (0 "LF ") + (1 "CRLF ") + (2 "CR ") + (_ "")) + (let* ((sys (coding-system-plist buffer-file-coding-system)) + (cat (plist-get sys :category)) + (sym (if (memq cat + '(coding-category-undecided coding-category-utf-8)) + 'utf-8 + (plist-get sys :name)))) + (upcase (symbol-name sym))))) + + (defface my-awesome-tray-module-buffer-encoding-face + '((t :inherit awesome-tray-green-face)) + "Buffer encoding module face." + :group 'awesome-tray) + + (add-to-list 'awesome-tray-module-alist + '("buffer-encoding" . (my-awesome-tray-module-buffer-encoding-info my-awesome-tray-module-buffer-encoding-face))) + + (awesome-tray-mode +1) + ) + +#+end_src + +** sort-tab 快速切换到其它 buffer + +#+begin_src elisp + (use-package sort-tab + :demand t + :straight (sort-tab :type git :host github :repo "manateelazycat/sort-tab") + :bind (("M-1" . sort-tab-select-visible-tab) + ("M-2" . sort-tab-select-visible-tab) + ("M-3" . sort-tab-select-visible-tab) + ("M-4" . sort-tab-select-visible-tab) + ("M-5" . sort-tab-select-visible-tab) + ("M-6" . sort-tab-select-visible-tab) + ("M-7" . sort-tab-select-visible-tab) + ("M-8" . sort-tab-select-visible-tab) + ("M-9" . sort-tab-select-visible-tab) + ) + :custom + (sort-tab-show-index-number t) + :config + (sort-tab-mode +1) + (with-eval-after-load 'ace-window + (add-to-list 'aw-ignored-buffers "*sort-tab*")) + ) +#+end_src + +** hydra 方便的快捷键设置 + +#+begin_src elisp + (use-package hydra + :straight t) + + (use-package hydra-posframe + :straight (hydra-posframe :type git :host github :repo "Ladicle/hydra-posframe") + :defer t + :custom + (hydra-posframe-poshandler 'posframe-poshandler-window-top-right-corner)) + + (use-package pretty-hydra + :straight t + :after hydra + :init + (cl-defun pretty-hydra-title (title &optional icon-type icon-name + &key face height v-adjust) + "Add an icon in the hydra title." + (let ((face (or face `(:foreground ,(face-background 'highlight)))) + (height (or height 1.0)) + (v-adjust (or v-adjust 0.0))) + (concat + (when (and icon-type icon-name) + (let ((f (intern (format "all-the-icons-%s" icon-type)))) + (when (fboundp f) + (concat + (apply f (list icon-name :face face :height height :v-adjust v-adjust)) + " ")))) + (propertize title 'face face)))) + :config + (pretty-hydra-define my-base-hydra () + ("" + (("h" backward-char "Backward") + ("j" next-line "Next") + ("k" previous-line "Previous") + ("l" forward-char "Forward") + ("" scroll-down-command "Scroll down") + ("" scroll-up-command "Scroll up") + ("m" set-mark-command "Set mark")))) + ) + + (use-package major-mode-hydra + :demand t + :straight t + :bind + ("M-m" . major-mode-hydra)) + +#+end_src + +** recentf 读取最近文件 + +#+begin_src elisp + (use-package recentf + :commands (recentf-open-files) + :hook (after-init . recentf-mode) + :bind ("C-x C-r" . recentf-open-files) + :init (setq recentf-max-saved-items 500 + recentf-exclude + '("\\.?cache" ".cask" "url" "COMMIT_EDITMSG\\'" "bookmarks" + "\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\|bmp\\|xpm\\)$" + "\\.?ido\\.last$" "\\.revive$" "/G?TAGS$" "/.elfeed/" + "^/tmp/" "^/var/folders/.+$" ; "^/ssh:" + (lambda (file) (file-in-directory-p file package-user-dir)))) + :config + (push (expand-file-name recentf-save-file) recentf-exclude)) + +#+end_src + +** popper 更方便的弹出窗口 + +#+begin_src elisp + (use-package popper + :straight t + :hook (emacs-startup . popper-mode) + :init + (setq popper-reference-buffers + '( + help-mode + rustic-cargo-run-mode + lsp-bridge-ref-mode + "^\\*eshell.*\\*$" eshell-mode + ;; emacs-aichat + "^\\*?[aA]ichat.*\\*$" + + "^\\*xref*\\*$" + "^\\*compilation*\\*$" + )) + :config + + (with-no-warnings + (defun my-popper-fit-window-height (win) + "Determine the height of popup window WIN by fitting it to the buffer's content." + (fit-window-to-buffer + win + (floor (frame-height) 3) + (floor (frame-height) 3))) + (setq popper-window-height #'my-popper-fit-window-height) + + (defun my-popper-window-popper-p (buffer) + "Whether `buffer' is popper window." + (when-let* ((window (caar popper-open-popup-alist)) + (buffer (cdar popper-open-popup-alist)) + (window-p (string= (buffer-name) (buffer-name buffer)))) + window)) + + (defun my-popper-close-window (&rest _) + "Close popper window via `C-g'." + ;; `C-g' can deactivate region + (when (and (called-interactively-p 'interactive) + (not (region-active-p)) + popper-open-popup-alist) + (let ((window (caar popper-open-popup-alist))) + (when (window-live-p window) + (delete-window window))))) + + (advice-add #'keyboard-quit :before #'my-popper-close-window)) + ) + + +#+end_src + +** autorevert 自动同步外部文件改变 + +#+begin_src elisp + (use-package autorevert + :config + (global-auto-revert-mode +1)) + +#+end_src + +** minibuffer 补全及增强 + +#+begin_src elisp + (use-package pinyinlib + :straight t + :defer t) + + (use-package orderless + :straight t + :custom (completion-styles '(orderless)) + :config + ;; 拼音搜索支持 + (defun completion--regex-pinyin (str) + (require 'pinyinlib) + (orderless-regexp (pinyinlib-build-regexp-string str))) + (add-to-list 'orderless-matching-styles 'completion--regex-pinyin) + ) + + (use-package vertico + :straight t + :config + (vertico-mode +1)) + + (use-package marginalia + :after (vertico) + :straight t + :config + (marginalia-mode +1)) + + + (use-package consult + :straight t + :defer t + :bind (("C-s" . consult-line) + ("C-x b" . consult-buffer) + ("C-x C-b" . consult-bookmark) + ("C-x C-i" . consult-imenu)) + :custom + (consult-preview-key nil) + (consult-buffer-sources '(consult--source-buffer consult--source-recent-file)) + :config + + (when sys-is-windows + (add-to-list 'process-coding-system-alist '("es.exe" gbk . gbk)) + (add-to-list 'process-coding-system-alist '("explorer" gbk . gbk)) + (add-to-list 'process-coding-system-alist '("rg" utf-8 . gbk)) + (setq consult-locate-args (encode-coding-string "es.exe -i -p -r" 'gbk)))) +#+end_src + +** vundo undo 增强 + +#+begin_src emacs-lisp + (use-package vundo + :straight t + :bind ("C-x u" . vundo)) +#+end_src + +** avy 快速移动光标到屏幕上任意字符 + +#+begin_src elisp + (use-package avy + :straight t + :bind (("M-'" . my/avy-goto-char-timer)) + :init + (defun my/avy-goto-char-timer (&optional arg) + "Make avy-goto-char-timer support pinyin" + (interactive "P") + (require 'pinyinlib) + (require 'avy) + (let ((avy-all-windows (if arg + (not avy-all-windows) + avy-all-windows))) + (avy-with avy-goto-char-timer + (setq avy--old-cands (avy--read-candidates + 'pinyinlib-build-regexp-string)) + (avy-process avy--old-cands)))) + + + :config + (setq avy-all-windows nil + avy-all-windows-alt t + avy-background t + avy-style 'pre)) +#+end_src + +** ace-window 快速切换到其它 window + +#+begin_src elisp + (use-package ace-window + :straight t + :pretty-hydra + ((:title (pretty-hydra-title "Window Management" 'faicon "th" :height 1.1 :v-adjust -0.1) + :foreign-keys warn :quit-key ("q" "C-g")) + ("Split" + (("r" split-window-right "horizontally" :exit t) + ("R" split-window-right "horizontally continue") + ("v" split-window-below "vertically" :exit t) + ("V" split-window-below "vertically continue")) + + "Resize" + (("h" shrink-window-horizontally "←") + ("j" enlarge-window "↓") + ("k" shrink-window "↑") + ("l" enlarge-window-horizontally "→") + ("n" balance-windows "balance" :exit t)) + + "Zoom" + (("+" text-scale-increase "in") + ("=" text-scale-increase "in") + ("-" text-scale-decrease "out") + ("0" (text-scale-increase 0) "reset")))) + :bind (("M-o" . ace-window) + ("C-c w" . ace-window-hydra/body)) + + ) +#+end_src + +** toggle-one-window 快速切换到单窗口 + +ref:https://github.com/manateelazycat/toggle-one-window + +#+begin_src elisp + (defvar my-window--configuration nil + "The window configuration use for `toggle-one-window'.") + + (defun my-window--one-window-p () + (equal 1 (length (cl-remove-if #'(lambda (w) + (and + (window-dedicated-p w) + (not (window-parameter w 'quit-restore)))) + (window-list))))) + + (defun my/toggle-one-window () + "Toggle between window layout and one window." + (interactive) + (cond + ;; 如果当前 buffer 所在 Window 是 popper + ((my-popper-window-popper-p (current-buffer)) + (if (my-window--one-window-p) + (when my-window--configuration + (set-window-configuration my-window--configuration) + (setq my-window--configuration nil)) + + (setq my-window--configuration (current-window-configuration)) + (let ((buffer (current-buffer))) + (other-window 1) + (delete-other-windows) + (switch-to-buffer buffer)))) + (t + (if (my-window--one-window-p) + (when my-window--configuration + (set-window-configuration my-window--configuration) + (setq my-window--configuration nil)) + (setq my-window--configuration (current-window-configuration)) + (delete-other-windows))))) + + (global-set-key (kbd "M-;") #'my/toggle-one-window) +#+end_src + +** yasnippet 快速插入代码片段 + +#+begin_src elisp + (use-package yasnippet + :straight t + :defer t + :config + (add-to-list 'yas-snippet-dirs my-private-snippets-dir) + (yas-reload-all)) + + (use-package yasnippet-snippets + :straight t + :after yasnippet) + +#+end_src + +** separedit 快速编辑 + +#+begin_src elisp + (use-package separedit + :straight t + :bind ("C-c '" . separedit)) +#+end_src + +** projectile.el 工程管理 + +#+begin_src elisp + (use-package projectile + :straight t + :bind (("" . projectile-compile-project) + ("" . projectile-run-project)) + :custom + (projectile-indexing-method 'alien) + :config + (projectile-mode +1) + (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) + + (defun my-project-root() + "Return project-root of current project." + (projectile-project-root)) + + ;; 生成 rsync 同步命令当前工程命令字符串 + (defun my-rsync-command() + "Rsync command." + (when-let* ((name (project-name (project-current))) + (root (expand-file-name (my-project-root))) + (cygdrive-root (replace-regexp-in-string "\\([cd]\\):" "/cygdrive/\\1" root))) + (format "rsync -avPz --password-file=/cygdrive/c/Users/xhcoding/rsyncd.secrets --exclude-from=%srsync_exclude.txt %s* xhcoding@%s::%s" cygdrive-root cygdrive-root (or my-remote-machine-ip "192.168.1.2") name))) + + ;; 生成使用 ssh 远程在当前工作工程目录下执行命令的字符串 + (defun my-ssh-shell-command (command) + "Shell command with ssh." + (when-let ((name (project-name (project-current)))) + (format "ssh xhcoding@192.168.1.2 \"cd /home/xhcoding/Code/%s ; %s\"" name command))) + + ;; 如果包括 rsync_exclude.txt ,远程执行编译命令 + (defun my*wrap-compile-command (command) + "" + (let ((root (my-project-root))) + (if (and (not (string-prefix-p "rsync" command)) + root + (file-exists-p (expand-file-name "rsync_exclude.txt" root))) + (concat (my-rsync-command) " && " (my-ssh-shell-command command)) + command))) + + (advice-add 'projectile-compilation-command :filter-return #'my*wrap-compile-command) + + ;; 如果包括 rsync_exclude.txt ,远程执行 run 命令 + (defun my*wrap-run-command (command) + "" + (let ((root (my-project-root))) + (if (and (not (string-prefix-p "ssh" command)) + root + (file-exists-p (expand-file-name "rsync_exclude.txt" root))) + (my-ssh-shell-command command) + command))) + (advice-add 'projectile-run-command :filter-return #'my*wrap-run-command) + + + ;; 注册工程 + (projectile-register-project-type 'xmake '("xmake.lua") + :project-file "xmake.lua" + :compile "xmake" + :test "xmake run" + :run "xmake run") + ) + + + +#+end_src + +** compile 增强 + +#+begin_src elisp + (use-package fancy-compilation + :straight t + :after compile + :config + (setq fancy-compilation-override-colors nil) + (fancy-compilation-mode +1)) +#+end_src + +** eshell 增强 + +*** eshell 基本配置 +#+begin_src elisp + (use-package eshell + :defer t + :custom + (eshell-kill-processes-on-exit t) + :config + + (my-cc--import-vcvars) + (setq exec-path (parse-colon-path (getenv "Path"))) + + ;; 默认为插入模式 + (add-to-list 'meow-mode-state-list '(eshell-mode . insert)) + + + ;; 配合 popper 实现 toggle 效果 + (defun my/eshell () + (interactive) + (if-let* ((window (caar popper-open-popup-alist)) + (buffer (cdar popper-open-popup-alist)) + (eshell-opened (string= eshell-buffer-name (buffer-name buffer)))) + (when (window-live-p window) + (delete-window window)) + (eshell))) + + ;; cat 高亮 + (defun my-eshell-cat-with-syntax-highlight (file) + "Like cat but with syntax highlight." + (with-temp-buffer + (insert-file-contents file) + (let ((buffer-file-name file)) + (delay-mode-hooks + (set-auto-mode) + (font-lock-ensure))) + (buffer-string))) + + (advice-add 'eshell/cat :override #'my-eshell-cat-with-syntax-highlight) + + ) +#+end_src + +*** eshell 历史记录设置 + +#+begin_src elisp + (use-package em-hist + :defer t + :custom + (eshell-history-size 10240) + (eshell-hist-ignoredups t) + (eshell-save-history-on-exit t)) +#+end_src + +*** esh-mode 设置 + +#+begin_src elisp + (use-package esh-mode + :bind (:map eshell-mode-map + ("C-r" . consult-history))) +#+end_src + +*** eshell-git-prompt 提示符美化 + +#+begin_src elisp + (use-package eshell-git-prompt + :straight t + :after esh-mode + :config + (eshell-git-prompt-use-theme 'powerline)) +#+end_src + +*** eshell-z 智能目录跳转 + +#+begin_src elisp + (use-package eshell + :defer t + :config + (require 'em-dirs) + (defvar my-eshell-z--table nil) + + (defvar my-eshell-z-file-name (expand-file-name "z" eshell-directory-name)) + + (defun my-eshell-z--load () + (setq my-eshell-z--table (make-hash-table :test 'equal)) + (when (file-exists-p my-eshell-z-file-name) + (dolist (element (with-temp-buffer + (insert-file-contents my-eshell-z-file-name) + (goto-char (point-min)) + (read (current-buffer)))) + (when (file-directory-p (car element)) + (puthash (car element) (cadr element) my-eshell-z--table))))) + + (defun my-eshell-z--save () + (let ((dir (file-name-directory my-eshell-z-file-name))) + (unless (file-exists-p dir) + (make-directory dir t)) + (with-temp-file my-eshell-z-file-name + (let ((result (list))) + (maphash #'(lambda (key value) + (when (> value 0) + (add-to-list 'result (list key (- value 0.1)))) + ) + my-eshell-z--table) + (prin1 result (current-buffer)))))) + + (defun my-eshell-z--update () + (let ((cur-dir default-directory)) + (if-let ((score (gethash cur-dir my-eshell-z--table))) + (puthash cur-dir (+ score 1) my-eshell-z--table) + (puthash cur-dir 1 my-eshell-z--table)))) + + (defun eshell/z (&rest args) + (let* ((first (car args)) + (result first)) + (if (not first) + (setq result "~") + (cond + ((string-match-p "^[\\.]+$" first) + (let ((target "")) + (cl-loop repeat (length first) do + (setq target (concat target "../"))) + (setq result target))) + ((string= "-" first) + (setq result first)) + (t (let ((regex "") + target-score + target-dir) + (dolist (arg args) + (setq regex (concat regex arg ".*"))) + (maphash #'(lambda (key value) + (when (string-match-p regex key) + (when (and target-score (> value target-score)) + (setq target-dir key + target-score value)) + (unless target-score + (setq target-dir key + target-score value)))) + my-eshell-z--table) + (if target-dir + (setq result target-dir) + (setq result args)) + )))) + ;; (message "result: %s" result) + (eshell/cd result))) + (add-hook 'eshell-mode-hook #'my-eshell-z--load) + (add-hook 'eshell-directory-change-hook #'my-eshell-z--update) + (add-hook 'kill-emacs-hook #'my-eshell-z--save) + + (defun eshell/zp (&rest args) + "Jump directory in current project." + (let* ((project-root (nth 2 (project-current)))) + (unless project-root + (setq project-root default-directory)) + (when-let* ((result (eshell-command-result + (concat "fd --type directory --absolute-path " (car args) " " project-root))) + (paths (split-string result "\n" t))) + (if (= (length paths) 1) + (eshell/cd (car paths)) + (eshell/cd (completing-read "Choose: " paths nil t)))))) + ) +#+end_src + +** fingertip 智能括号插入 + +#+begin_src emacs-lisp + (use-package fingertip + :straight (fingertip :type git :host github :repo "manateelazycat/fingertip") + :defer t + :hook ((prog-mode toml-ts-mode) my-enable-pair-parents) + :bind (:map fingertip-mode-map + ("(" . fingertip-open-round) + ("[" . fingertip-open-bracket) + ("{" . fingertip-open-curly) + (")" . fingertip-close-round) + ("]" . fingertip-close-bracket) + ("}" . fingertip-close-curly) + ("=" . fingertip-equal) + ("\"" . fingertip-double-quote) + ("SPC" . fingertip-space) + ("RET". fingertip-newline) + ("C-k" . fingertip-kill) + ("M-\"" . fingertip-wrap-double-quote) + ("M-[" . fingertip-wrap-bracket) + ("M-{" . fingertip-wrap-curly) + ("M-(" . fingertip-wrap-round) + ("M-]" . fingertip-unwrap) + ("M-n" . fingertip-jump-right) + ("M-p" . fingertip-jump-left) + ("M-RET" . fingertip-jump-out-pair-and-newline)) + :init + (defun my-enable-pair-parents () + (if (treesit-parser-list) + (fingertip-mode) + (electric-pair-mode)))) +#+end_src + +** color-rg 快速搜索重构 + +#+begin_src elisp + (use-package color-rg + :straight (color-rg :type git :host github :repo "manateelazycat/color-rg") + :defer t + :commands (color-rg-search-symbol-in-project color-rg-search-input-in-project) + :custom + (color-rg-search-no-ignore-file nil) + :config + (add-to-list 'meow-mode-state-list '(color-rg-mode . motion))) +#+end_src + +** lsp-bridge 代码补全 + +#+begin_src elisp + + (use-package markdown-mode + :straight t + :defer t) + + (use-package lsp-bridge + :straight (lsp-bridge :type git :host github :repo "manateelazycat/lsp-bridge" + :files ("*") + :build nil) + :defer t + :bind (:map lsp-bridge-mode-map + ([remap xref-find-definitions] . lsp-bridge-find-def) + ([remap xref-find-references] . lsp-bridge-find-references) + ([remap xref-go-back] . lsp-bridge-find-def-return)) + :init + + ;; 手动添加到 load-path + (add-to-list 'load-path (straight--repos-dir "lsp-bridge")) + + (setq lsp-bridge-org-babel-lang-list nil) + + ;; https://tecosaur.github.io/emacs-config/config.html#lsp-support-src + (cl-defmacro my-lsp-org-babel-enable (lang) + "Support LANG in org source code block." + (cl-check-type lang string) + (let* ((edit-pre (intern (format "org-babel-edit-prep:%s" lang))) + (intern-pre (intern (format "lsp--%s" (symbol-name edit-pre))))) + `(progn + (defun ,intern-pre (info) + (let ((file-name (->> info caddr (alist-get :file)))) + (unless file-name + (setq file-name (expand-file-name "OrgBabel/org-src-babel" my-code-dir)) + (write-region (point-min) (point-max) file-name)) + (setq buffer-file-name file-name) + (my-enable-lsp-bridge))) + (put ',intern-pre 'function-documentation + (format "Enable lsp-bridge-mode in the buffer of org source block (%s)." + (upcase ,lang))) + (if (fboundp ',edit-pre) + (advice-add ',edit-pre :after ',intern-pre) + (progn + (defun ,edit-pre (info) + (,intern-pre info)) + (put ',edit-pre 'function-documentation + (format "Prepare local buffer environment for org source block (%s)." + (upcase ,lang)))))))) + + (with-eval-after-load 'org + (dolist (lang '("c" "cpp" "python" "rust")) + (eval `(my-lsp-org-babel-enable ,lang)))) + + + :config + (push '(scss-mode . "vscode-css-language-server") lsp-bridge-single-lang-server-mode-list) + (push '(json-ts-mode . "vscode-json-language-server") lsp-bridge-single-lang-server-mode-list) + + (setq lsp-bridge-user-langserver-dir my-etc-dir + lsp-bridge-user-multiserver-dir my-etc-dir) + + + (setq acm-enable-tabnine nil + acm-enable-quick-access t + lsp-bridge-enable-hover-diagnostic t) + ) +#+end_src + +启动 lsp-bridge + +#+begin_src elisp + (defun my-enable-lsp-bridge() + "Enable lsp bridge mode." + (require 'yasnippet) + (yas-minor-mode) + (require 'lsp-bridge) + (lsp-bridge-mode)) + + +#+end_src + +** highlight-parentheses 高亮外层括号 + +#+begin_src elisp + (use-package highlight-parentheses + :straight t + :hook (prog-mode . highlight-parentheses-mode)) +#+end_src + +** evil-nerd-commenter 智能注释代码 + +#+begin_src elisp + (use-package evil-nerd-commenter + :straight t + :bind ("C-/" . evilnc-comment-or-uncomment-lines)) +#+end_src + +** apheleia 智能格式化代码 + +#+begin_src elisp + (use-package apheleia + :straight t + :defer t + :bind ("C-M-\\" . my/format-buffer-or-region) + :config + (defun my/format-buffer-or-region () + (interactive) + (cond + ((derived-mode-p 'emacs-lisp-mode) + (if (region-active-p) + (indent-region (region-beginning) (region-end)) + (indent-region (point-min) (point-max)))) + ((and (boundp 'lsp-bridge-mode) lsp-bridge-mode) + (call-interactively #'lsp-bridge-code-format)) + (t + (call-interactively #'apheleia-format-buffer)))) + ) +#+end_src + +** wuco 拼写检查 + +#+begin_src elisp + (use-package ispell + :if full? + :config + (setq ispell-program-name "hunspell" + ispell-dictionary "en_US" + ispell-local-dictionary-alist '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)) + ispell-hunspell-dict-paths-alist `(("en_US" ,(expand-file-name "dict/en_US.aff" my-etc-dir))) + ispell-hunspell-dictionary-alist '(("en_US")))) + + (use-package wucuo + :if full? + :straight t + :defer t) + +#+end_src + +** git 配置 + +*** magit + +#+begin_src elisp + (use-package magit + :straight (magit :type git :host github :repo "magit/magit" + :files ("lisp/magit" + "lisp/magit*.el" + "lisp/git-rebase.el" + "docs/magit.texi" + "docs/AUTHORS.md" + "LICENSE" + "Documentation/magit.texi" ; temporarily for stable + "Documentation/AUTHORS.md" ; temporarily for stable + )) + :bind ("C-x g" . magit-status) + :config + (when sys-is-windows + (setenv "GIT_ASKPASS" "git-gui--askpass"))) + + (use-package ssh-agency + :straight t + :after magit) + + +#+end_src + +*** 显示当前行的最后提交信息 + +#+begin_src emacs-lisp + (use-package git-messenger + :straight t + :bind (:map vc-prefix-map + ("p" . git-messenger:popup-message) + :map git-messenger-map + ("m" . git-messenger:copy-message)) + :init + (setq git-messenger:show-detail t + git-messenger:use-magit-popup t) + + (defface posframe-border + `((t (:inherit region))) + "Face used by the `posframe' border." + :group 'posframe) + :config + (with-no-warnings + (with-eval-after-load 'hydra + (defhydra git-messenger-hydra (:color blue) + ("s" git-messenger:popup-show "show") + ("c" git-messenger:copy-commit-id "copy hash") + ("m" git-messenger:copy-message "copy message") + ("," (catch 'git-messenger-loop (git-messenger:show-parent)) "go parent") + ("q" git-messenger:popup-close "quit"))) + + (defun my-git-messenger:format-detail (vcs commit-id author message) + (if (eq vcs 'git) + (let ((date (git-messenger:commit-date commit-id)) + (colon (propertize ":" 'face 'font-lock-comment-face))) + (concat + (format "%s%s %s \n%s%s %s\n%s %s %s \n" + (propertize "Commit" 'face 'font-lock-keyword-face) colon + (propertize (substring commit-id 0 8) 'face 'font-lock-comment-face) + (propertize "Author" 'face 'font-lock-keyword-face) colon + (propertize author 'face 'font-lock-string-face) + (propertize "Date" 'face 'font-lock-keyword-face) colon + (propertize date 'face 'font-lock-string-face)) + (propertize (make-string 38 ?─) 'face 'font-lock-comment-face) + message + (propertize "\nPress q to quit" 'face '(:inherit (font-lock-comment-face italic))))) + (git-messenger:format-detail vcs commit-id author message))) + + (defun my-git-messenger:popup-message () + "Popup message with `posframe', `pos-tip', `lv' or `message', and dispatch actions with `hydra'." + (interactive) + (let* ((hydra-hint-display-type 'message) + (vcs (git-messenger:find-vcs)) + (file (buffer-file-name (buffer-base-buffer))) + (line (line-number-at-pos)) + (commit-info (git-messenger:commit-info-at-line vcs file line)) + (commit-id (car commit-info)) + (author (cdr commit-info)) + (msg (git-messenger:commit-message vcs commit-id)) + (popuped-message (if (git-messenger:show-detail-p commit-id) + (my-git-messenger:format-detail vcs commit-id author msg) + (cl-case vcs + (git msg) + (svn (if (string= commit-id "-") + msg + (git-messenger:svn-message msg))) + (hg msg))))) + (setq git-messenger:vcs vcs + git-messenger:last-message msg + git-messenger:last-commit-id commit-id) + (run-hook-with-args 'git-messenger:before-popup-hook popuped-message) + (git-messenger-hydra/body) + (cond ((and (fboundp 'posframe-workable-p) (posframe-workable-p)) + (let ((buffer-name "*git-messenger*")) + (posframe-show buffer-name + :string (concat (propertize "\n" 'face '(:height 0.3)) + popuped-message + "\n" + (propertize "\n" 'face '(:height 0.3))) + :left-fringe 8 + :right-fringe 8 + :max-width (round (* (frame-width) 0.62)) + :max-height (round (* (frame-height) 0.62)) + :internal-border-width 1 + :internal-border-color (face-background 'posframe-border nil t) + :background-color (face-background 'tooltip nil t)) + (unwind-protect + (push (read-event) unread-command-events) + (posframe-hide buffer-name)))) + ((and (fboundp 'pos-tip-show) (display-graphic-p)) + (pos-tip-show popuped-message)) + ((fboundp 'lv-message) + (lv-message popuped-message) + (unwind-protect + (push (read-event) unread-command-events) + (lv-delete-window))) + (t (message "%s" popuped-message))) + (run-hook-with-args 'git-messenger:after-popup-hook popuped-message))) + (advice-add #'git-messenger:popup-close :override #'ignore) + (advice-add #'git-messenger:popup-message :override #'my-git-messenger:popup-message))) +#+end_src + +*** smerge-mode 解决冲突 + +#+begin_src elisp + (use-package smerge-mode + :ensure nil + :pretty-hydra + ((:title (pretty-hydra-title "Smerge" 'octicon "diff") + :color pink :quit-key "q") + ("Move" + (("n" smerge-next "next") + ("p" smerge-prev "previous")) + "Keep" + (("b" smerge-keep-base "base") + ("u" smerge-keep-upper "upper") + ("l" smerge-keep-lower "lower") + ("a" smerge-keep-all "all") + ("RET" smerge-keep-current "current") + ("C-m" smerge-keep-current "current")) + "Diff" + (("<" smerge-diff-base-upper "upper/base") + ("=" smerge-diff-upper-lower "upper/lower") + (">" smerge-diff-base-lower "upper/lower") + ("R" smerge-refine "refine") + ("E" smerge-ediff "ediff")) + "Other" + (("C" smerge-combine-with-next "combine") + ("r" smerge-resolve "resolve") + ("k" smerge-kill-current "kill") + ))) + :bind (:map smerge-mode-map + ("C-c m" . smerge-mode-hydra/body))) +#+end_src + +** visual-regexp 可视化的正则替换 + +#+begin_src elisp + (use-package visual-regexp + :straight t + :defer t) +#+end_src + +** expand-region 递增选区 + +#+begin_src emacs-lisp + (use-package expand-region + :straight t + :bind (("C-=" . er/expand-region)) + :config + (defun treesit-mark-bigger-node () + (let* ((root (treesit-buffer-root-node)) + (node (treesit-node-descendant-for-range root (region-beginning) (region-end))) + (node-start (treesit-node-start node)) + (node-end (treesit-node-end node))) + ;; Node fits the region exactly. Try its parent node instead. + (when (and (= (region-beginning) node-start) (= (region-end) node-end)) + (when-let ((node (treesit-node-parent node))) + (setq node-start (treesit-node-start node) + node-end (treesit-node-end node)))) + (set-mark node-end) + (goto-char node-start))) + + (add-to-list 'er/try-expand-list 'treesit-mark-bigger-node) + ) + +#+end_src + +** openwith 外部工具打开文件 + +#+begin_src elisp + (use-package openwith + :if sys-is-windows + :straight t + :custom + (openwith-associations + '(("\\.\\(doc\\|docs\\|xls\\|xlsx\\|ppt\\|pptx\\)$" + "explorer.exe" (file)))) + :config + (openwith-mode +1)) +#+end_src + +** 启用 treesit + +#+begin_src elisp + (setq major-mode-remap-alist + '((c-mode . c-ts-mode) + (c++-mode . c++-ts-mode) + (c-or-c++-mode . c-or-c++-ts-mode) + (cmake-mode . cmake-ts-mode) + (conf-toml-mode . toml-ts-mode) + (csharp-mode . csharp-ts-mode) + (css-mode . css-ts-mode) + (dockerfile-mode . dockerfile-ts-mode) + (go-mode . go-ts-mode) + (java-mode . java-ts-mode) + (json-mode . json-ts-mode) + (js-json-mode . json-ts-mode) + (javascript-mode . js-ts-mode) + (python-mode . python-ts-mode) + (sh-mode . bash-ts-mode))) + + (setq treesit-language-source-alist + '((bash . ("https://github.com/tree-sitter/tree-sitter-bash")) + (c . ("https://github.com/tree-sitter/tree-sitter-c")) + (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp")) + (css . ("https://github.com/tree-sitter/tree-sitter-css")) + (cmake . ("https://github.com/uyha/tree-sitter-cmake")) + (csharp . ("https://github.com/tree-sitter/tree-sitter-c-sharp.git")) + (dockerfile . ("https://github.com/camdencheek/tree-sitter-dockerfile")) + (elisp . ("https://github.com/Wilfred/tree-sitter-elisp")) + (go . ("https://github.com/tree-sitter/tree-sitter-go")) + (gomod . ("https://github.com/camdencheek/tree-sitter-go-mod.git")) + (html . ("https://github.com/tree-sitter/tree-sitter-html")) + (java . ("https://github.com/tree-sitter/tree-sitter-java.git")) + (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript")) + (json . ("https://github.com/tree-sitter/tree-sitter-json")) + (lua . ("https://github.com/Azganoth/tree-sitter-lua")) + (make . ("https://github.com/alemuller/tree-sitter-make")) + (markdown . ("https://github.com/MDeiml/tree-sitter-markdown" nil "tree-sitter-markdown/src")) + (ocaml . ("https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src")) + (org . ("https://github.com/milisims/tree-sitter-org")) + (python . ("https://github.com/tree-sitter/tree-sitter-python")) + (php . ("https://github.com/tree-sitter/tree-sitter-php")) + (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src")) + (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "tsx/src")) + (ruby . ("https://github.com/tree-sitter/tree-sitter-ruby")) + (rust . ("https://github.com/tree-sitter/tree-sitter-rust")) + (sql . ("https://github.com/m-novikov/tree-sitter-sql")) + (vue . ("https://github.com/merico-dev/tree-sitter-vue")) + (yaml . ("https://github.com/ikatyang/tree-sitter-yaml")) + (toml . ("https://github.com/tree-sitter/tree-sitter-toml")) + (zig . ("https://github.com/GrayJack/tree-sitter-zig")))) +#+end_src + +** ts-fold 基于 treesit 的代码折叠 + +#+begin_src emacs-lisp + (use-package ts-fold + :if full? + :straight (ts-fold :type git :host github :repo "AndrewSwerlick/ts-fold" :branch "andrew-sw/treesit-el-support") + :bind ("M-z" . ts-fold-toggle) + :config + ;; elisp support + (add-to-list 'ts-fold-range-alist '(emacs-lisp-mode + (function_definition . ts-fold-range-seq) + (list . ts-fold-range-seq) + (special_form . ts-fold-range-seq)))) +#+end_src + +** elisp 配置 + +#+begin_src emacs-lisp + (use-package elisp-mode + :hook ((emacs-lisp-mode . my-enable-elisp-dev)) + :config + (defun my-enable-elisp-dev () + (my-enable-lsp-bridge) + (treesit-parser-create 'elisp))) +#+end_src + +** C++ 开发配置 +*** citre + +#+begin_src elisp + (use-package citre + :disabled t + :straight t + :defer t) +#+end_src + +*** cmake-ts-mode 设置 + +#+begin_src emacs-lisp + (use-package cmake-ts-mode + :mode "CMakeLists.txt\\'" + :hook (cmake-ts-mode . my-enable-lsp-bridge)) +#+end_src + +*** c-ts-mode 设置 + +#+begin_src elisp + (use-package c-ts-mode + :hook ((c-ts-mode c++-ts-mode) . my-cc-enable-dev) + :custom + (c-ts-mode-indent-offset 4) + :config + (defun my-cc-enable-dev() + (setq-local lsp-bridge-completion-hide-characters '(";" "(" ")" "[" "]" "{" "}" "," "\"")) + (my-enable-lsp-bridge)) + + (defun my-cc--treesit-match-namespace (node parent bol &rest _) + (string= (treesit-node-type (treesit-node-parent parent)) "namespace_definition")) + + (defun my-cc--indent-style () + "Indent rules supported by `c-ts-mode'" + (let* ((mode (if (derived-mode-p 'c-ts-mode) 'c 'cpp)) + (common (alist-get 'gnu (c-ts-mode--indent-styles mode)))) + `( + ;; namespace + (my-cc--treesit-match-namespace parent-bol 0) + ,@common))) + + (setq c-ts-mode-indent-style #'my-cc--indent-style) + + ) +#+end_src + +*** 导入 VS 环境变量 + +#+begin_src elisp + (defconst my-cc--msvc-env-vars + '( + "DevEnvDir" + "Framework40Version" + "FrameworkDir" + "FrameworkDIR32" + "FrameworkDIR64" + "FrameworkVersion" + "FrameworkVersion32" + "FrameworkVersion64" + "INCLUDE" + "LIB" + "LIBPATH" + "NETFXSDKDir" + "PATH" + "UCRTVersion" + "UniversalCRTSdkDir" + "user_inputversion" + "VCIDEInstallDir" + "VCINSTALLDIR" + "VCToolsInstallDir" + "VCToolsRedistDir" + "VCToolsVersion" + "VS170COMNTOOLS" + "VisualStudioVersion" + "VSINSTALLDIR" + "WindowsLibPath" + "WindowsSdkBinPath" + "WindowsSdkDir" + "WindowsSDKLibVersion" + "WindowsSDKVersion" + "WindowsSDK_ExecutablePath_x64" + "WindowsSDK_ExecutablePath_x86" + ;;/* These are special also need to be cached */ + "CL" + "_CL_" + "LINK" + "_LINK_" + "TMP" + "UCRTCONTEXTROOT" + "VCTARGETSPATH" + ) + "List of environment variables required for Visual C++ to run as expected for a VS installation.") + + ;; 导入 vs2022 community 64位构建环境变量 + (defun my-cc--import-vcvars () + "Import the environment variables corresponding to a VS dev batch file." + (let* ((common-dir "C:/Program Files/Microsoft Visual Studio/2022/Community/Common7/Tools") + (devbat "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat") + (args "amd64") + (major-version "17") + (bat-path (expand-file-name "cmake-tools-vcvars.bat" (temporary-file-directory))) + (env-file-path (concat bat-path ".env")) + (vars (make-hash-table :test 'equal))) + (setq bat + (format "@echo off + cd /d \"%%~dp0\" + set \"VS%s0COMNTOOLS=%s\" + set \"INCLUDE=\" + call \"%s\" %s + setlocal enableextensions enabledelayedexpansion + cd /d \"%%~dp0\" + %s" major-version common-dir devbat args + (mapconcat (lambda (env-var) (format "if DEFINED %s echo %s := %%%s%% >> %s" env-var env-var env-var env-file-path)) + my-cc--msvc-env-vars "\n"))) + (with-temp-file bat-path + (insert bat)) + (shell-command-to-string (concat "cmd.exe /c " bat-path)) + + (if (not (file-exists-p env-file-path)) + nil + (dolist (line (with-temp-buffer + (insert-file-contents env-file-path) + (split-string (buffer-string) "\n" t))) + (let ((var (split-string line " := " t))) + (puthash (string-trim (car var)) (string-trim (cadr var)) vars))) + (if (not (gethash "INCLUDE" vars)) + nil + (maphash #'(lambda (key value) + (setenv key value)) + vars))))) +#+end_src + +** Rust 开发配置 + +#+begin_src elisp + (use-package rust-ts-mode + :mode "\\.rs\\'" + :hook (rust-ts-mode . my-enable-lsp-bridge)) +#+end_src + +** python 开发配置 + +#+begin_src emacs-lisp + (use-package python + :hook (python-ts-mode . my-enable-lsp-bridge)) +#+end_src + +** java 开发配置 + +#+begin_src elisp + (use-package java-ts-mode + :if full? + :hook (java-ts-mode . my-enable-java-dev) + :config + (defun my-enable-java-dev () + ;; treesit-forward-sexp 工作异常 + (setq forward-sexp-function nil) + + (require 'lsp-bridge-jdtls) + (setq-local lsp-bridge-get-single-lang-server-by-project 'lsp-bridge-get-jdtls-server-by-project) + (my-enable-lsp-bridge)) + + ;; jdtls 目录 + (defconst my--java-jdtls-root-dir (expand-file-name "apps/jdtls/current" (getenv "SCOOP"))) + (defconst my--java-jdtls-conf-dir (expand-file-name "config_win" my--java-jdtls-root-dir)) + (defconst my--java-jdtls-equinox-path (car (directory-files (expand-file-name "plugins" my--java-jdtls-root-dir) t "org.eclipse.equinox.launcher_.*\\.jar"))) + (defconst my--java-jdtls-command + `("java" + "-Declipse.application=org.eclipse.jdt.ls.core.id1" + "-Dosgi.bundles.defaultStartLevel=4" + "-Declipse.product=org.eclipse.jdt.ls.core.product" + "-Dosgi.checkConfiguration=true" + ,(format "-Dosgi.sharedConfiguration.area=%s" my--java-jdtls-conf-dir) + "-Dosgi.sharedConfiguration.area.readOnly=true" + "-Dosgi.configuration.cascaded=true" + "-Xms1G" + "--add-modules=ALL-SYSTEM" + "--add-opens" + "java.base/java.util=ALL-UNNAMED" + "--add-opens" + "java.base/java.lang=ALL-UNNAMED" + "-jar" + ,my--java-jdtls-equinox-path + )) + + (defun my--lsp-bridge-jdtls-init-config (project-path filepath) + "Initialize JDTLS configuration" + (message "init config: %s " project-path) + (let* ((json-object-type 'plist) + (config (json-read-file lsp-bridge-jdtls-default-file)) + (config-file (lsp-bridge-jdtls-config-file project-path filepath)) + (data-directory (lsp-bridge-jdtls-project-data-dir project-path filepath))) + + ;; An additional JVM option (can be used multiple times. Note, use with equal sign. For example: --jvm-arg=-Dlog.level=ALL + + (plist-put config :command my--java-jdtls-command) + + (when (listp lsp-bridge-jdtls-jvm-args) + (let ((jvm-args (mapcar (lambda (arg) (concat "--jvm-arg=" arg)) + lsp-bridge-jdtls-jvm-args))) + + (plist-put config :command (vconcat (plist-get config :command) + jvm-args)))) + + ;; Add the `-data` parameter to the startup parameter + (plist-put config :command (vconcat (plist-get config :command) `("-data" ,data-directory))) + + (make-directory (file-name-directory config-file) t) + + (with-temp-file config-file + (insert (json-encode config))))) + + (advice-add 'lsp-bridge-jdtls-init-config :override 'my--lsp-bridge-jdtls-init-config) + + ) + + + (use-package kotlin-ts-mode + :disabled not-full? + :straight (kotlin-ts-mode :type git :host gitlab :repo "bricka/emacs-kotlin-ts-mode") + :mode "\\.kts\\'") +#+end_src + +** web 开发 + +*** Javascript + +#+begin_src elisp + (use-package js + :if full? + :hook (js-ts-mode . my-js-enable-dev) + :config + (defun my-js-enable-dev () + ;; treesit-forward-sexp 工作异常 + (setq forward-sexp-function nil) + (my-enable-lsp-bridge)) + ) +#+end_src + +*** Typescript + +#+begin_src emacs-lisp + (use-package typescript-ts-mode + :if full? + :mode "\\.ts\\'" + :hook (typescript-ts-base-mode . my-enable-lsp-bridge)) +#+end_src + +*** HTML + +#+begin_src emacs-lisp + (use-package web-mode + :if full? + :straight t + :mode "\\.\\(phtml\\|php\\|[gj]sp\\|as[cp]x\\|erb\\|djhtml\\|html?\\|hbs\\|ejs\\|jade\\|swig\\|tm?pl\\|vue\\)$" + :hook (web-mode . my-enable-lsp-bridge) + :custom + (web-mode-markup-indent-offset 2) + (web-mode-css-indent-offset 2) + (web-mode-code-indent-offset 2) + (web-mode-enable-auto-indentation nil) + ) +#+end_src + +*** css + + +#+begin_src elisp + (use-package css-mode + :hook (css-ts-mode . my-enable-lsp-bridge) + :custom + (css-indent-offset 2)) + + (use-package scss-mode + :hook (scss-mode . my-enable-lsp-bridge)) +#+end_src + +** json-ts-mode + +#+begin_src elisp + (use-package json-ts-mode + :hook (json-ts-mode . my-enable-json-dev) + :custom + (json-ts-mode-indent-offset 2) + :config + (defun my-enable-json-dev () + (my-enable-lsp-bridge)) + + (defun my-json-generate-language-server-json () + (with-temp-file (expand-file-name "vscode-json-language-server.json" my-etc-dir) + (url-insert-file-contents "https://www.schemastore.org/api/json/catalog.json") + (let* ((catalog (json-parse-string (buffer-substring-no-properties (point-min) (point-max)) :object-type 'plist)) + (schemas (plist-get catalog :schemas)) + (exclude-pattern ".*\.\\(cff\\|cjs\\|js\\|mjs\\|toml\\|yaml\\|yml\\)$") + (json-validation )) + (mapc #'(lambda (schema) + (when-let* ((url (plist-get schema :url)) + (file-match (plist-get schema :fileMatch)) + (filtered-file-match (seq-filter #'(lambda (match) + (and (not (string-prefix-p "!" match)) + (not (string-match-p exclude-pattern match)))) + file-match))) + (add-to-list 'json-validation `((:url . ,url) (:fileMatch ., filtered-file-match))))) + schemas) + + (let* ((ls '(:name "vscode-json-language-server" :languageId "json" :command ("vscode-json-language-server" "--stdio"))) + (json `(:schemas ,json-validation)) + (settings `(:json ,json)) + (json-encoding-pretty-print t)) + (plist-put ls :settings settings) + (erase-buffer) + (goto-char (point-min)) + (insert (json-encode ls)))))) + ) +#+end_src + +** qml 开发 + +#+begin_src elisp + (use-package qml-ts-mode + :if full? + :straight (qml-ts-mode :type git :host github :repo "xhcoding/qml-ts-mode") + :hook (qml-ts-mode . my-enable-lsp-bridge) + :custom + (qml-ts-mode-indent-offset 4)) +#+end_src + +** lua 开发 + +#+begin_src elisp + (use-package lua-mode + :if full? + :straight t) +#+end_src + +** plantuml 支持 + +#+begin_src emacs-lisp + (use-package plantuml-mode + :if full? + :straight t + :defer t + :custom + (plantuml-jar-path (expand-file-name "plantuml.jar" my-lib-dir)) + (plantuml-default-exec-mode 'jar)) +#+end_src + +** dape 调试 + +#+begin_src elisp + (use-package dape + :straight (dape :type git :host github :repo "svaante/dape")) +#+end_src + +** dash-docs 查询 dash 文档 + +#+begin_src elisp + (use-package dash-docs + :if full? + :straight t + :commands (my/dash-docs-search) + :bind (([remap apropos-documentation] . my/dash-docs-search) + (" D" . my/dash-docs-search-docset)) + :defer t + :init + ;; 初始化文档 + (dolist (mode-docsets + '((c++-ts-mode-hook . ("C++" "Boost" "Qt_6")) + (qml-ts-mode-hook . ("Qt_6")) + (cmake-ts-mode-hook . ("CMake")) + (rust-ts-mode-hook . ("Rust")) + (python-ts-mode-hook . ("Python_3")) + )) + (let ((hook (car mode-docsets)) + (docsets (cdr mode-docsets))) + (add-hook hook `(lambda () + (setq-local dash-docs-docsets ',docsets) + )))) + + :custom + (dash-docs-enable-debugging nil) + (dash-docs-browser-func 'eaf-open-browser) + :config + (when sys-is-windows + (setq dash-docs-docsets-path (expand-file-name "persist/zeal/docsets" (getenv "SCOOP")))) + + ;; fix can not open.. + ;; ref: https://github.com/dash-docs-el/dash-docs/pull/11 + (defun dash-docs-sql (db-path sql) + "Run in the db located at DB-PATH the SQL command and parse the results. + If there are errors, print them in `dash-docs-debugging-buffer'" + (dash-docs-parse-sql-results + (with-output-to-string + (let ((error-file (when dash-docs-enable-debugging + (make-temp-file "dash-docs-errors-file")))) + (call-process "sqlite3" nil (list standard-output error-file) nil + ;; args for sqlite3: + "-list" "-init" null-device db-path sql) + + ;; display errors, stolen from emacs' `shell-command` function + (when (and error-file (file-exists-p error-file)) + (if (< 0 (nth 7 (file-attributes error-file))) + (with-current-buffer (dash-docs-debugging-buffer) + (let ((pos-from-end (- (point-max) (point)))) + (or (bobp) + (insert "\f\n")) + ;; Do no formatting while reading error file, + ;; because that can run a shell command, and we + ;; don't want that to cause an infinite recursion. + (format-insert-file error-file nil) + ;; Put point after the inserted errors. + (goto-char (- (point-max) pos-from-end))) + (display-buffer (current-buffer)))) + (delete-file error-file)))))) + + ;; 搜索文档 + (defun my/dash-docs-search (&optional pattern) + "Search doc." + (interactive) + (when-let ((search-pattern + (or pattern + (let* ((current-symbol + (if (use-region-p) + (buffer-substring-no-properties (region-beginning) (region-end)) + (thing-at-point 'symbol))) + (input-string + (string-trim + (read-string + (format "Pattern (%s): " current-symbol) nil)))) + (when (string-blank-p input-string) + (setq input-string current-symbol)) + input-string)))) + (dash-docs-create-buffer-connections) + (dash-docs-create-common-connections) + (when-let ((results (dash-docs-search search-pattern)) + (select t) + (select-index -1) + (select-result t)) + (setq select (completing-read "Select: " + (let ((index 0)) + (mapcar (lambda (result) + (setq index (+ index 1)) + (format "%s. %s" index (car result))) + results)) + nil t)) + (setq select-index (- (string-to-number (car (split-string select "\\. "))) 1)) + (setq select-result (nth select-index results)) + (dash-docs-browse-url (cdr select-result))))) + + (defun my/dash-docs-search-docset (&optional docset) + "Search doc in `docset'" + (interactive (list (dash-docs-read-docset + "Docset" + (dash-docs-installed-docsets)))) + (unless (boundp 'dash-docs-docsets) + (setq-local dash-docs-docsets `(,docset))) + + (let ((old-dash-docs-docsets dash-docs-docsets)) + (unwind-protect + (progn + (setq-local dash-docs-docsets `(,docset)) + (call-interactively #'my/dash-docs-search) + (setq-local dash-docs-docsets old-dash-docs-docsets)) + (setq-local dash-docs-docsets old-dash-docs-docsets)))) + + ) +#+end_src + +** shrface 让 eww 的阅读体验更好 + +#+begin_src elisp + (use-package shrface + :if full? + :straight t + :defer t + :config + (shrface-basic) + (shrface-trial) + (shrface-default-keybindings) ; setup default keybindings + (setq shrface-href-versatile t)) + +#+end_src + +** eww 配置 + +#+begin_src elisp + (use-package eww + :if full? + :defer t + :mode-hydra + (eww-mode + (:title (pretty-hydra-title "eww" 'oction "browser") :color blue :inherit (my-base-hydra/heads) + :pre (unless hydra-posframe-mode + (hydra-posframe-mode))) + ("A" + (("d" eww-download "Download") + ("t" popweb-dict-eudic-input "Translate" :exit nil)))) + :init + (add-hook 'eww-after-render-hook #'shrface-mode) + :config + (require 'shrface) + (require 'popweb) + ) + +#+end_src + +** olivetti 居中显示内容 + +#+begin_src elisp + (use-package olivetti + :if full? + :straight t + :defer t + :hook (prog-mode text-mode outline-mode special-mode elfeed-show-mode) + ) +#+end_src + +** org 配置 +*** org-mode 美化设置 + +#+begin_src elisp + (use-package org + :if full? + :defer t + :straight t + :hook (org-mode . my--org-prettify-symbols) + :custom-face + ;; 设置Org mode标题以及每级标题行的大小 + (org-document-title ((t (:height 1.75 :weight bold)))) + (org-level-1 ((t (:height 1.2 :weight bold)))) + (org-level-2 ((t (:height 1.15 :weight bold)))) + (org-level-3 ((t (:height 1.1 :weight bold)))) + (org-level-4 ((t (:height 1.05 :weight bold)))) + (org-level-5 ((t (:height 1.0 :weight bold)))) + (org-level-6 ((t (:height 1.0 :weight bold)))) + (org-level-7 ((t (:height 1.0 :weight bold)))) + (org-level-8 ((t (:height 1.0 :weight bold)))) + (org-level-9 ((t (:height 1.0 :weight bold)))) + ;; 设置代码块用上下边线包裹 + (org-block-begin-line ((t (:underline t :background unspecified)))) + (org-block-end-line ((t (:overline t :underline nil :background unspecified)))) + :custom + ;; 标题行美化 + (org-fontify-whole-heading-line t) + ;; 设置标题行折叠符号 + (org-ellipsis " ▾") + ;; TODO标签美化 + (org-fontify-todo-headline t) + ;; DONE标签美化 + (org-fontify-done-headline t) + ;; 引用块美化 + (org-fontify-quote-and-verse-blocks t) + ;; 隐藏宏标记 + (org-hide-macro-markers t) + ;; 隐藏强调标签 + (org-hide-emphasis-markers t) + ;; 高亮latex语法 + (org-highlight-latex-and-related '(native script entities)) + ;; 以UTF-8显示 + (org-pretty-entities t) + ;; 当启用缩进模式时自动隐藏前置星号 + (org-indent-mode-turns-on-hiding-stars t) + ;; 自动启用缩进 + (org-startup-indented nil) + ;; 根据标题栏自动缩进文本 + (org-adapt-indentation nil) + ;; 自动显示图片 + (org-startup-with-inline-images t) + ;; 默认以Overview的模式展示标题行 + (org-startup-folded 'overview) + ;; 允许字母列表 + (org-list-allow-alphabetical t) + ;; 编辑时检查是否在折叠的不可见区域 + (org-fold-catch-invisible-edits 'smart) + ;; 上标^下标_是否需要特殊字符包裹,这里设置需要用大括号包裹 + (org-use-sub-superscripts '{}) + :config + + (create-fontset-from-fontset-spec + (font-xlfd-name + (font-spec :family "等距更纱黑体 SC" + :weight 'regular + :slant 'normal + :registry "fontset-orgtable"))) + + (set-fontset-font "fontset-orgtable" '(#x0 . #xffff) + (font-spec :family "等距更纱黑体 SC" + :weight 'regular + :slant 'normal)) + + (set-face-attribute 'org-table nil :fontset "fontset-orgtable" :font "fontset-orgtable") + + (defun my--org-prettify-symbols () + (setq prettify-symbols-alist + (mapcan (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) + '( + ("#+begin_src" . ?✎) + ("#+end_src" . ?□) + ("#+results:" . ?💻) + ("#+date:" . ?📅) + ("#+author:" . ?👤) + ("#+title:" . ?📓) + ("#+identifier:" . ?🆔) + ("#+hugo_tags:" . ?📍) + ("#+hugo_categories:" . ?📁) + ("#+hugo_locale:" . ?🌐) + ("#+hugo_draft:" . ?🚮) + ("#+hugo_custom_front_matter:" . ?📝) + ("#+begin_quote" . ?«) + ("#+end_quote" . ?») + ))) + (setq prettify-symbols-unprettify-at-point t) + (prettify-symbols-mode 1)) + + ;; 中文强调标记可用 + (setcar (nthcdr 0 org-emphasis-regexp-components) " \t('\"{[:nonascii:]") + (setcar (nthcdr 1 org-emphasis-regexp-components) "- \t.,:!?;'\")}\\[[:nonascii:]") + (org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) + (org-element-update-syntax) + + + ;; 设置标题行之间总是有空格;列表之间根据情况自动加空格 + (setq org-blank-before-new-entry '((heading . t) + (plain-list-item . auto) + )) + ) + + (use-package org-modern + :if full? + :straight t + :hook (org-mode . org-modern-mode) + :config + ;; 额外的行间距,0.1表示10%,1表示1px + (setq-default line-spacing 0.1) + ;; 复选框美化 + (setq org-modern-checkbox + '((?X . #("▢✓" 0 2 (composition ((2))))) + (?- . #("▢–" 0 2 (composition ((2))))) + (?\s . #("▢" 0 1 (composition ((1))))))) + ;; 列表符号美化 + (setq org-modern-list + '((?- . "•") + (?+ . "◦") + (?* . "▹"))) + ;; 代码块类型美化,我们使用了 `prettify-symbols-mode' + (setq org-modern-block-name nil) + ;; #+关键字美化,我们使用了 `prettify-symbols-mode' + (setq org-modern-keyword nil) + ;; 关闭表格美化 + (setq org-modern-table nil) + ) + + (use-package org-appear + :if full? + :straight t + :hook (org-mode . org-appear-mode) + :config + (setq org-appear-autolinks t) + (setq org-appear-autosubmarkers t) + (setq org-appear-autoentities t) + (setq org-appear-autokeywords t) + (setq org-appear-inside-latex t) + ) +#+end_src + +*** org 基本配置 + +#+begin_src elisp + (use-package org + :if full? + :defer t + :straight t + :custom + (org-directory my-org-dir) + (org-modules '(ol-wl)) + (org-structure-template-alist + '(("q" . "quote\n") + ("s" . "src") + ("e" . "src elisp\n") + ("c" . "src cpp\n") + ("h" . "export html")) + )) +#+end_src + +*** org babel 设置 + +#+begin_src elisp + (use-package org + :if full? + :defer t + :straight t + :custom + (org-confirm-babel-evaluate nil) + (org-export-use-babel nil) + (org-src-lang-modes '(("C" . c-ts) + ("C++" . c++-ts) + ("asymptote" . asy) + ("bash" . sh) + ("beamer" . latex) + ("calc" . fundamental) + ("cpp" . c++-ts) + ("ditaa" . artist) + ("desktop" . conf-desktop) + ("dot" . fundamental) + ("elisp" . emacs-lisp) + ("ocaml" . tuareg) + ("screen" . shell-script) + ("shell" . sh) + ("sqlite" . sql) + ("toml" . conf-toml))) + :config + (org-babel-do-load-languages 'org-babel-load-languages + '((emacs-lisp . t) + (perl . t) + (python . t) + (ruby . t) + (js . t) + (css . t) + (sass . t) + (C . t) + (java . t) + (plantuml . t))) + ;; C 执行支持 :stdin 数据 + (defun my*org-babel-execute-add-stdin(args) + (let* ((body (nth 0 args)) + (params (nth 1 args)) + (stdin (cdr (assq :stdin params))) + (cmdline (cdr (assq :cmdline params))) + (stdin-file (expand-file-name "input_data.txt" (temporary-file-directory))) + (data)) + (when stdin + (setq data + (save-excursion + (org-babel-goto-named-src-block stdin) + (org-element-property :value (org-element-at-point)))) + (with-temp-file stdin-file + (insert data)) + (setq cmdline (concat (or cmdline "") " < " stdin-file)) + (setf (alist-get :cmdline params) cmdline)) + `(,body ,params) + )) + (advice-add #'org-babel-C-execute :filter-args 'my*org-babel-execute-add-stdin) + + ) +#+end_src + +*** gtd 配置 + +#+begin_src elisp + (use-package org-gtd + :if full? + :straight t + :after org + :bind (("C-c d c" . org-gtd-capture) + ("C-c d e" . org-gtd-engage) + ("C-c d p" . org-gtd-process-inbox) + ("C-c d n" . org-gtd-show-all-next) + ("C-c d s" . org-gtd-review-stuck-projects) + :map org-gtd-clarify-map + ("C-c c" . org-gtd-organize)) + :init + (setq org-gtd-update-ack "3.0.0") + :custom + (org-gtd-directory (expand-file-name "gtd" my-org-dir)) + :config + (org-gtd-mode +1) + (setq org-edna-use-inheritance t) + (org-edna-mode +1)) +#+end_src + +*** export 设置 + +#+begin_src emacs-lisp + (use-package org + :if full? + :straight t + :defer t + :hook (org-mode . my--set-org-html-head-extra) + :custom + (org-export-with-broken-links t) + :config + (defun my--set-org-html-head-extra () + "Set org html head extra" + (let ((path (expand-file-name "custom-head.html" my-etc-dir))) + (when (file-exists-p path) + (setq org-html-head-extra (with-temp-buffer + (insert-file-contents path) + (buffer-string)))))) + ) + + (use-package htmlize + :if full? + :straight t + :after org) + +#+end_src + +*** 生成 TOC + +#+begin_src emacs-lisp + (use-package toc-org + :if full? + :straight t + :commands (toc-org-insert-toc) + :custom + (toc-org-max-depth 3)) +#+end_src + +*** org-contrib wanderlust 支持 + +#+begin_src elisp + (use-package org-contrib + :if full? + :straight t + :after org) +#+end_src + +*** 增量载入 org 包 + +#+begin_src emacs-lisp + (when full? + (doom-load-packages-incrementally + '(calendar find-func format-spec org-macs org-compat org-faces org-entities + org-list org-pcomplete org-src org-footnote org-macro ob org org-agenda + org-capture org-gtd))) +#+end_src + +** 知识管理及博客配置 + +*** denote + +#+begin_src elisp + (use-package denote + :if full? + :straight t + :custom + (denote-directory (expand-file-name "denote" "D:/Code/Test")) + (denote-known-keywords '("emacs" "life" "cs")) + (denote-org-front-matter + "#+title: %1$s + ,#+date: %2$s + ,#+author: xhcoding + ,#+identifier: %4$s + ,#+hugo_locale: zh + ,#+hugo_tags: %3$s + ,#+hugo_categories: %3$s + ,#+hugo_draft: true + + " + ) + :bind (("C-c n n" . denote)) + ) +#+end_src + +*** org-roam + +#+begin_src emacs-lisp + (use-package emacsql-sqlite-builtin + :disabled t + :straight t + :defer t + ) + + (use-package org-roam + :disabled t + :straight t + :custom + (org-roam-directory (expand-file-name "org-roam" my-org-dir)) + :bind (("C-c n l" . org-roam-buffer-toggle) + ("C-c n f" . org-roam-node-find) + ("C-c n g" . org-roam-graph) + ("C-c n i" . org-roam-node-insert) + ("C-c n c" . org-roam-capture) + ;; Dailies + ("C-c n j" . org-roam-dailies-capture-today)) + :init + (setq org-roam-v2-ack t) + (setq org-roam-database-connector 'sqlite-builtin) + :config + (org-roam-setup) + + (setq org-roam-capture-templates + '(("d" "default" plain "%?" + :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" + " + ,#+title: ${title} + ,#+date: %<%Y-%m-%d> + ,#+author: xhcoding + ,#+hugo_locale: zh + ,#+hugo_tags: + ,#+hugo_categories: + ,#+hugo_draft: false + ") + :unnarrowed t))) + ) + + +#+end_src + +*** hugo 配置 + +#+begin_src emacs-lisp + ;; 博客相关配置 + (defconst my-blog-root-dir + "~/Blog/" + "Blog root directory.") + + (defconst my-blog-img-dir + (expand-file-name "images/" my-archives-dir) + "Blog's image directory.") + + (defconst my-blog-res-url + "https://images.xhcoding.cn/blog/" + "Blog's source address.") + + (defconst my-blog-post-url + "https://xhcoding.cn/post/" + "Blog's post address.") + + (use-package ox-hugo + :if full? + :straight t + :after org + :custom + (org-hugo-base-dir my-blog-root-dir) + (org-hugo-section "post") + (org-hugo-default-section-directory "post") + (org-hugo-auto-set-lastmod t) + :config + (defun my-blog--blog-image-file-path-p (raw-path) + (let ((file-path (expand-file-name raw-path))) + (equal (string-match-p (regexp-quote (expand-file-name my-blog-img-dir)) file-path) 0))) + + ;; ~/Documents/Blog/images/1.png ==> https://images.xhcoding.cn/1.png + + (defun my-blog--convert-image-url (raw-path) + (let ((file-path (expand-file-name raw-path))) + (concat my-blog-res-url (string-trim-left file-path my-blog-img-dir)))) + + ;; id:xxxxxx ==> https://xhcoding.cn/post/aaaaa + (defun my-blog--convert-publish-url (id) + (let ((file-name (file-name-base (org-roam-node-file (org-roam-node-from-id id))))) + (concat my-blog-post-url file-name))) + + ;; ~/Documents/Blog/images/1.png ==> https://images.xhcoding.cn/1.png + ;; id:xxxxxx ==> https://xhcoding.cn/post/aaaaa + (defun my-blog*export-blog-url (args) + (let* ((link (nth 0 args)) + (desc (nth 1 args)) + (info (nth 2 args)) + (type (org-element-property :type link)) + (raw-path (org-element-property :path link))) + (cond + ((and (string-equal type "file") (my-blog--blog-image-file-path-p raw-path)) + (progn + (let* ((image-url (my-blog--convert-image-url raw-path)) + (new-link (org-element-put-property link :path image-url ))) + `(,new-link ,desc ,info)))) + ((string-equal type "id") + (progn + (let* ((post-url (my-blog--convert-publish-url raw-path)) + (new-link)) + (setq new-link (org-element-put-property link :path post-url)) + (setq new-link (org-element-put-property new-link :type "file")) + `(,new-link ,desc, info)))) + (t `(,link ,desc ,info)) + ))) + + (advice-add #'org-hugo-link :filter-args #'my-blog*export-blog-url) + + ;; front matter author => authors + (defun my-blog*convert-author-to-authors(front-matter) + "Convert author to authors." + (let ((ret (replace-regexp-in-string "author" "authors" front-matter))) + ret)) + + (advice-add #'org-hugo--get-front-matter :filter-return #'my-blog*convert-author-to-authors) + ) + + (use-package easy-hugo + :if full? + :straight t + :defer t + :config + (require 'org-roam) + (setq easy-hugo-basedir (expand-file-name my-blog-root-dir) + easy-hugo-postdir (expand-file-name org-roam-directory) + easy-hugo-org-header t + easy-hugo-github-deploy-script "deploy.bat")) + +#+end_src + +*** 上传博客图片到腾讯云对象存储 + +#+begin_src emacs-lisp + (defun my-blog/upload-images-to-tencent-cos () + "Upload blog images dir to tencent cos." + (interactive) + (call-process-shell-command + (concat + "coscmd upload -rs " + my-blog-img-dir + " blog/" + ))) + +#+end_src + +*** 自动在中英文插入空格 + +#+begin_src elisp + (use-package pangu-spacing + :if full? + :straight t + :after org + :hook (org-mode . pangu-spacing-mode) + :custom + (pangu-spacing-real-insert-separtor t)) +#+end_src + +*** org-download 下载图片 + +#+begin_src elisp + (use-package org-download + :if full? + :straight t + :after org + :config + (when sys-is-windows + (setq org-download-screenshot-method "irfanview /capture=4 /convert=\"%s\"" + org-download-display-inline-images 'posframe + org-download-abbreviate-filename-function 'expand-file-name)) + + (setq-default org-download-image-dir my-blog-img-dir + org-download-heading-lvl nil) + + + ;; 截图的名称不要总是 screenshot + (defun my/org-download-screenshot () + "Capture screenshot and insert the resulting file. + The screenshot tool is determined by `org-download-screenshot-method'." + (interactive) + (let* ((screenshot-dir (file-name-directory org-download-screenshot-file)) + (org-file-path (buffer-file-name)) + (org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path))) + (new-screenshot-name (concat org-file-name ".png")) + (new-screenshot-path (expand-file-name new-screenshot-name screenshot-dir))) + (when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path)) + (setq new-screenshot-name + (substring new-screenshot-name (+ 1 (string-match-p "-" new-screenshot-name))) + new-screenshot-path + (expand-file-name new-screenshot-name screenshot-dir))) + (make-directory screenshot-dir t) + (if (functionp org-download-screenshot-method) + (funcall org-download-screenshot-method + org-download-screenshot-file) + (shell-command-to-string + (format org-download-screenshot-method + org-download-screenshot-file))) + (when (file-exists-p org-download-screenshot-file) + (rename-file org-download-screenshot-file new-screenshot-path) + (org-download-image new-screenshot-path) + (delete-file new-screenshot-path)))) + + (defun my/org-download-clipboard() + "Download from clipboard" + (interactive) + (let ((org-download-screenshot-method "irfanview /clippaste /convert=\"%s\"")) + (my/org-download-screenshot))) + + ;; 将图片保存到当前 buffer 名称目录下 + ;; ref: https://github.com/abo-abo/org-download/issues/195 + (defun my-org-download-method (link) + (let* ((filename + (file-name-nondirectory + (car (url-path-and-query + (url-generic-parse-url link))))) + (org-file-path (buffer-file-name)) + (org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path))) + (dirname (expand-file-name org-file-name my-blog-img-dir))) + (when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path)) + (setq dirname (expand-file-name + (substring org-file-name (+ 1 (string-match-p "-" org-file-name))) + my-blog-img-dir))) + (make-directory dirname t) + (expand-file-name (funcall org-download-file-format-function filename) dirname))) + (setq org-download-method 'my-org-download-method)) + + +#+end_src + +** eaf 配置 + +#+begin_src elisp + (use-package eaf + :if full? + :straight (emacs-application-framework :type git :host github :repo "emacs-eaf/emacs-application-framework" + :files ("*") + :build nil) + :defer t + :commands (eaf-open eaf-open-browser eaf-open-this-buffer eaf-open-pdf-from-history eaf-open-cloud-music) + :init + ;; 手动添加到 load-path + (add-to-list 'load-path (straight--repos-dir "emacs-application-framework")) + :config + (setq eaf-proxy-type "http") + (setq eaf-proxy-host "127.0.0.1") + (setq eaf-proxy-port "7890") + + (setq eaf-webengine-default-zoom 2.5) + (when sys-is-windows + (setq eaf-chrome-bookmark-file (expand-file-name "~/AppData/Local/Microsoft/Edge/User Data/Default/Bookmarks")) + + (defun my-eaf--enable-python-utf8-mode (environment) + (append (list "PYTHONUTF8=1") environment)) + (advice-add 'eaf--build-process-environment :filter-return #'my-eaf--enable-python-utf8-mode)) + (require 'eaf-browser) + (require 'eaf-pdf-viewer) + (require 'eaf-org-previewer) + (require 'eaf-markdown-previewer) + (require 'eaf-music-player)) + +#+end_src + +** popweb 配置 + +#+begin_src elisp + (use-package popweb + :if full? + :straight (popweb :type git :host github :repo "manateelazycat/popweb" :build nil) + :commands (popweb-dict-eudic-input popweb-import-browser-cookies) + :bind (" t" . popweb-dict-eudic-input) + :init + (let ((repo (straight--repos-dir "popweb"))) + (add-to-list 'load-path repo) + (add-to-list 'load-path (expand-file-name "extension/dict" repo)) + ) + :config + (require 'popweb-dict) + + ;; 欧陆词典,可以登录后加入生词,方便手机同步 + (popweb-dict-create "eudic" "https://dict.eudic.net/dicts/en/%s" + (concat + "document.getElementsByTagName('header')[0].style.display = 'none';" + "document.getElementById('head-bar').style.display = 'none';" + "document.getElementById('head-bk').style.display = 'none';" + "document.getElementById('scrollToTop').style.display = 'none';" + "document.getElementById('bodycontent').children[4].style.display = 'none';" + )) + ) +#+end_src + +** dictionary-overlay 方便阅读英文文章 + +#+begin_src elisp + (use-package websocket + :if full? + :straight t + :defer t) + + (use-package websocket-bridge + :if full? + :straight (websocket-bridge :type git :host github :repo "ginqi7/websocket-bridge") + :defer t) + + + (use-package dictionary-overlay + :if full? + :straight (dictionary-overlay :type git :host github :repo "ginqi7/dictionary-overlay" + :build nil) + :defer t + :init + (add-to-list 'load-path (straight--repos-dir "dictionary-overlay")) + :custom + (dictionary-overlay-python "python")) +#+end_src + +** 邮件配置 + +*** Wanderlust 邮件前端 + +Wanderlust + offlineimap3 + mu + +#+begin_src emacs-lisp + (use-package wl + :if full? + :straight (wanderlust) + :defer t + :hook ((wl . meow-motion-mode)) + :bind (:map wl-folder-mode-map + (("q" . wl-folder-suspend))) + :init + (setq wl-init-file (expand-file-name "wl.el" my-private-dir) + wl-folders-file (expand-file-name "folders.wl" my-private-dir) + wl-address-file (expand-file-name "address.wl" my-private-dir)) + :config + (if (boundp 'mail-user-agent) + (setq mail-user-agent 'wl-user-agent)) + (if (fboundp 'define-mail-user-agent) + (define-mail-user-agent + 'wl-user-agent + 'wl-user-agent-compose + 'wl-draft-send + 'wl-draft-kill + 'mail-send-hook)) + (setq wl-quicksearch-folder "[]") + + (setq wl-message-ignored-field-list + '(".") + wl-message-visible-field-list + '("^\\(To\\|Cc\\):" + "^Subject:" + "^\\(From\\|Reply-To\\):" + "^\\(Posted\\|Date\\):" + "^Organization:" + "^X-\\(Face\\(-[0-9]+\\)?\\|Weather\\|Fortune\\|Now-Playing\\):") + wl-message-sort-field-list + (append wl-message-sort-field-list + '("^Reply-To" "^Posted" "^Date" "^Organization"))) + + ;; windows 上 mu find 返回的路径以 /cygdrive/ 开头,我们需要自己处理一下 + (defun my--elmo-search-parse-filename-list () + (let (bol locations) + (goto-char (point-min)) + (while (not (eobp)) + (beginning-of-line) + (when (and elmo-search-use-drive-letter + (looking-at "^\\(/cygdrive/\\)?\\([A-Za-z]\\)\\([:|]\\)?/")) + (replace-match "/\\2:/") + (beginning-of-line)) + (unless (looking-at "^file://") + (insert "file://") + (beginning-of-line)) + (setq bol (point)) + (end-of-line) + (setq locations (cons (buffer-substring bol (point)) locations)) + (forward-line)) + (nreverse locations))) + + + (elmo-search-register-engine + 'mu-msys 'local-file + :prog "mu" + :args '("find" elmo-search-split-pattern-list "--fields" "l") + :charset 'utf-8 + :parser 'my--elmo-search-parse-filename-list) + + (setq elmo-search-default-engine 'mu-msys) + + ;; mu 的输入要用 gbk 编码,不然无法输入中文 + (add-to-list 'process-coding-system-alist '("mu" utf-8 . gbk)) + + ;; mime 附件保存目录 + (setq mime-save-directory (expand-file-name "mails" my-archives-dir)) + ) + + +#+end_src + +调用 compose-mail 前先 require wanderlust + +#+begin_src emacs-lisp + (when full? + + (defun my-*require-wanderlust (&rest _) + (require 'wl)) + + (advice-add 'compose-mail :before #'my-*require-wanderlust)) +#+end_src + +*** alert-toast 邮件通知 + +#+begin_src emacs-lisp + (use-package alert-toast + :if full? + :straight t + :after wl + :config + (defun my--notify-new-mail-arrived (number) + (alert-toast-notify `(:title "Wanderlust" :message ,(format "你有 %s 封未读邮件" number)))) + + (add-hook 'wl-biff-new-mail-hook #'my--notify-new-mail-arrived) + + ) +#+end_src + +** elfeed RSS 订阅 + +#+begin_src emacs-lisp + (use-package elfeed + :if full? + :straight t + :defer t + :config + (defun my-eaf-elfeed-open-url () + "Display the currently selected item in an eaf buffer." + (interactive) + (let ((entry (elfeed-search-selected :ignore-region))) + (require 'elfeed-show) + (when (elfeed-entry-p entry) + ;; Move to next feed item. + (elfeed-untag entry 'unread) + (elfeed-search-update-entry entry) + (unless elfeed-search-remain-on-entry (forward-line)) + (my/toggle-one-window) + (eaf-open-browser (elfeed-entry-link entry)) + ))) + ) + + (use-package elfeed-org + :if full? + :straight t + :after elfeed + :config + (elfeed-org) + (setq rmh-elfeed-org-files `(,(expand-file-name "elfeed.org" my-org-dir)))) +#+end_src + +** leetcode.el 刷题 + +#+begin_src elisp + (use-package leetcode + :if full? + :straight (leetcode :type git :host github :repo "xhcoding/leetcode.el") + :defer t + :custom + (leetcode-prefer-language "cpp") + (leetcode-save-solutions t) + (leetcode-directory (expand-file-name "Project/LeetCode" my-code-dir)) + :config + ;; 在 leetcode 里关闭自动补全 + (defun my-*after-leetcode--start-coding (problem problem-info) + (let-alist problem + (let* ((title (leetcode-problem-title problem-info)) + (code-buf-name (leetcode--get-code-buffer-name title))) + (with-current-buffer (leetcode--get-code-buffer code-buf-name) + (when lsp-bridge-mode + (lsp-bridge-mode -1)))))) + + (advice-add 'leetcode--start-coding :after 'my-*after-leetcode--start-coding) + + ) +#+end_src + +** LaTex + +#+begin_src elisp + (use-package tex + :if full? + :straight (auctex) + :defer t + :config + (add-to-list 'TeX-command-list '("XeLaTeX" "%`xelatex --synctex=1%(mode)%' %t" TeX-run-TeX nil t)) + (add-to-list 'TeX-view-program-list '("eaf" eaf-pdf-synctex-forward-view)) + (add-to-list 'TeX-view-program-selection '(output-pdf "eaf"))) +#+end_src + +** emacs-aichat AI 对话 + +#+begin_src elisp + (use-package async-await + :straight t + :defer t) + + (use-package aichat + :if full? + :defer t + :load-path (lambda () (expand-file-name "Project/emacs-aichat" my-code-dir)) + :config + (setq aichat-bingai-proxy "localhost:7890" + aichat-openai-proxy "localhost:7890") + (aichat-toggle-debug) + (aichat-bingai-prompt-create "translator" + :input-prompt "请翻译: " + :text-format "我想让你充当翻译员,我会用任何语言与你交谈,你会检测我说的的语言,如果我说的是中文,你就翻译成英文;如果我说的不是中文,你就翻译成英文。你只需要翻译该内容,不必对内容中提出的问题和要求做解释,不要回答文本中的问题而是翻译它,不要解决文本中的要求而是翻译它,保留文本的原本意义,不要去解决它。你的回答里只需要翻译后的内容,不要有任何其它词,只能是翻译后的内容。我的第一句话是:\n%s" + :chat t + :assistant t + :replace-or-insert t) + + (aichat-bingai-prompt-create "coder" + :input-prompt "代码: " + :text-format "我想让你充当计算机教授,请向我解释下面这段代码的作用:\n%s" + :chat t) + + (aichat-bingai-prompt-create "refactor" + :input-prompt "代码: " + :text-format "我想让你充当计算机教授,请帮我重构下面这段代码,重构后的代码性能要更好,可读性要更高,如果必要的话,可以加一些注释。你的回答里只需要返回重构后的代码,不要有其它解释,只能是重构后的代码:\n%s" + :replace-or-insert t) + ) +#+end_src + +** which-key 按键提示 + +#+begin_src elisp + (use-package which-key + :straight t + :hook (after-init . which-key-mode)) +#+end_src + +** meow 模式编辑 + +#+begin_src elisp + (use-package meow + :straight t + :demand t + :hook (after-init . meow-global-mode) + :config + + (with-eval-after-load 'rime + (dolist (p '(meow-normal-mode-p meow-motion-mode-p meow-keypad-mode-p)) + (add-to-list 'rime-disable-predicates p))) + + (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty) + + (setq meow-char-thing-table + '((?r . round) + (?c . curly) + (?s . string) + (?b . buffer) + (?d . defun))) + + (setq meow-use-clipboard t) + + (meow-leader-define-key + ;; Use SPC (0-9) for digit arguments. + '("1" . meow-digit-argument) + '("2" . meow-digit-argument) + '("3" . meow-digit-argument) + '("4" . meow-digit-argument) + '("5" . meow-digit-argument) + '("6" . meow-digit-argument) + '("7" . meow-digit-argument) + '("8" . meow-digit-argument) + '("9" . meow-digit-argument) + '("0" . meow-digit-argument) + '("?" . meow-cheatsheet) + + '("b" . consult-buffer) + '("r" . consult-ripgrep) + + '("f" . projectile-find-file) + '("p" . projectile-switch-project) + + '("/" . evilnc-comment-or-uncomment-lines) + + '("w" . ace-window-hydra/body) + ) + + (meow-normal-define-key + ;; char move + '("j" . meow-next) + '("J" . meow-next-expand) + '("k" . meow-prev) + '("K" . meow-prev-expand) + '("l" . meow-right) + '("L" . meow-right-expand) + '("h" . meow-left) + '("H" . meow-left-expand) + + ;; word move + '("e" . meow-next-word) + '("E" . meow-next-symbol) + '("b" . meow-back-word) + '("B" . meow-back-symbol) + + ;; line move + '("x" . meow-line) + '("X" . meow-goto-line) + + ;; thing move + '("," . meow-inner-of-thing) + '("." . meow-bounds-of-thing) + '("[" . meow-beginning-of-thing) + '("]" . meow-end-of-thing) + '("o" . meow-block) + '("O" . meow-to-block) + + ;; jump + '("n" . meow-search) + '("f" . meow-find) + '("v" . meow-visit) + + ;; action + '("i" . meow-insert) + '("I" . meow-open-above) + '("a" . meow-append) + '("A" . meow-open-below) + '("y" . meow-save) + '("Y" . meow-sync-grab) + '("c" . meow-change) + '("r" . meow-replace) + '("R" . meow-swap-grab) + '("p" . meow-yank) + '("s" . meow-kill) + '("d" . meow-delete) + '("D" . meow-backward-delete) + '("G" . meow-grab) + '("m" . meow-join) + '("t" . meow-till) + '("u" . undo) + '("U" . meow-undo) + '("w" . meow-mark-word) + '("W" . meow-mark-symbol) + '("z" . meow-pop-selection) + + ;; others + '("0" . meow-expand-0) + '("9" . meow-expand-9) + '("8" . meow-expand-8) + '("7" . meow-expand-7) + '("6" . meow-expand-6) + '("5" . meow-expand-5) + '("4" . meow-expand-4) + '("3" . meow-expand-3) + '("2" . meow-expand-2) + '("1" . meow-expand-1) + '("-" . negative-argument) + '(";" . meow-reverse) + '("g" . meow-cancel-selection) + '("q" . meow-quit) + '("'" . repeat) + '("" . ignore) + ) + ) +#+end_src + +** 启动 emacs server + +#+begin_src elisp + (when sys-is-windows + (unless (daemonp) + (server-start))) +#+end_src + +** 载入私有配置文件 + +#+begin_src elisp + (ignore-errors + (load (expand-file-name "config.el" my-private-dir))) + +#+end_src + diff --git a/build.sh b/build.sh index 3f1e2e9..b8a6c2d 100644 --- a/build.sh +++ b/build.sh @@ -7,6 +7,7 @@ readonly LOAD_PATH=$EMACS_DIR/etc PACK_NAME=emacs-x PACK_SYSTEM=x86_64-linux +export EMACS_CONFIG_MODE=minimal if [[ ! -z $1 ]] ; then PACK_NAME=$1 @@ -16,8 +17,8 @@ if [[ ! -z $2 ]] ; then PACK_SYSTEM=$1 fi -PACK_FILENAME="${PACK_NAME}-${PACK_SYSTEM}.tar.gz" -INSTALLER_FILENAME="${PACK_NAME}-${PACK_SYSTEM}-installer.run" +PACK_FILENAME="${PACK_NAME}-${EMACS_CONFIG_MODE}-${PACK_SYSTEM}.tar.gz" +INSTALLER_FILENAME="${PACK_NAME}-${EMACS_CONFIG_MODE}-${PACK_SYSTEM}-installer.run" echo "Emacs dir: $EMACS_DIR" echo "Pack name: $PACK_FILENAME" diff --git a/install.sh b/install.sh index 4ac1142..fecb870 100644 --- a/install.sh +++ b/install.sh @@ -1,6 +1,6 @@ #!/bin/bash set -e -readonly SCRIPT_LINES=51 +readonly SCRIPT_LINES=53 readonly TEMP_PATH=/tmp/emacs-x-temp.tar.gz INSTALL_DIR=/opt/emacs-x @@ -40,6 +40,8 @@ export GUIX_LOCPATH=${INSTALL_DIR}/opt/emacs/lib/locale export PATH=${INSTALL_DIR}/opt/emacs/bin:${PATH} source ${INSTALL_DIR}/opt/emacs/etc/profile +export EMACS_CONFIG_MODE=minimal + emacs --init-directory=${INSTALL_DIR}/opt/emacs/share/emacs.d "\$@" EOF