A simple Emacs config on Windows/WSL/Linux
- 下载配置文件
git clone https://github.com/xhcoding/emacs.d.git ~/.emacs.d
- 运行
org-tangle.bat
生成.el
文件 - 启动 emacs
- 下载 Release 里面的
emacs-x-minimal-x86_64-linux-installer.run
安装包 - 添加可执行权限
chmod +x emacs-x-minimal-x86_64-linux-installer.run
- 执行
./emacs-x-minimal-x86_64-linux-installer.run
将 emacs 安装到/opt
目录下 emacs-x
启动 emacs
- 安装方法
- early-init.el
- emacs 配置
- 载入 custom 文件
- 定义一些固定的常量
- 常用的工具函数
- 基本设置
- gcmh 优化垃圾回收
- auto-save 自动保存
- rime 智能的中文输入法
- 字体和主题
- ligature 连字体
- awesome-tray 底部信息显示
- sort-tab 快速切换到其它 buffer
- hydra 方便的快捷键设置
- recentf 读取最近文件
- popper 更方便的弹出窗口
- autorevert 自动同步外部文件改变
- minibuffer 补全及增强
- vundo undo 增强
- avy 快速移动光标到屏幕上任意字符
- ace-window 快速切换到其它 window
- toggle-one-window 快速切换到单窗口
- yasnippet 快速插入代码片段
- separedit 快速编辑
- projectile.el 工程管理
- compile 增强
- eshell 增强
- fingertip 智能括号插入
- color-rg 快速搜索重构
- lsp-bridge 代码补全
- highlight-parentheses 高亮外层括号
- evil-nerd-commenter 智能注释代码
- apheleia 智能格式化代码
- wuco 拼写检查
- git 配置
- visual-regexp 可视化的正则替换
- expand-region 递增选区
- openwith 外部工具打开文件
- 启用 treesit
- elisp 配置
- C++ 开发配置
- Rust 开发配置
- python 开发配置
- java 开发配置
- web 开发
- json-ts-mode
- qml 开发
- lua 开发
- plantuml 支持
- dape 调试
- dash-docs 查询 dash 文档
- shrface 让 eww 的阅读体验更好
- eww 配置
- olivetti 居中显示内容
- org 配置
- 知识管理及博客配置
- eaf 配置
- popweb 配置
- dictionary-overlay 方便阅读英文文章
- 邮件配置
- elfeed RSS 订阅
- leetcode.el 刷题
- LaTex
- emacs-aichat AI 对话
- which-key 按键提示
- meow 模式编辑
- 启动 emacs server
- 载入私有配置文件
Emacs 启动过程的早期加载。
;;; early-init.el --- early init -*- lexical-binding: t no-byte-compile: t; -*-
(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?))
(when debug?
(setq toggle-debug-on-error t))
(setq gc-cons-threshold most-positive-fixnum)
(unless (or (daemonp) noninteractive init-file-debug)
(let ((old-file-name-handler-alist file-name-handler-alist))
(setq file-name-handler-alist nil)
(add-hook 'emacs-startup-hook
(lambda ()
"Recover file name handlers."
(setq file-name-handler-alist
(delete-dups (append file-name-handler-alist
old-file-name-handler-alist)))))))
(setq package-enable-at-startup nil)
(setq frame-inhibit-implied-resize t)
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
(when full?
(push '(fullscreen . fullscreen) default-frame-alist))
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name
"straight/repos/straight.el/bootstrap.el"
(or (bound-and-true-p straight-base-dir)
user-emacs-directory)))
(bootstrap-version 7))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
(setq straight-vc-git-default-clone-depth 1)
from: https://github.com/doomemacs/doomemacs/blob/master/lisp/doom-start.el
(defvar doom-incremental-packages '(t)
"A list of packages to load incrementally after startup. Any large packages
here may cause noticeable pauses, so it's recommended you break them up into
sub-packages. For example, `org' is comprised of many packages, and can be
broken up into:
(doom-load-packages-incrementally
'(calendar find-func format-spec org-macs org-compat
org-faces org-entities org-list org-pcomplete org-src
org-footnote org-macro ob org org-clock org-agenda
org-capture))
This is already done by the lang/org module, however.
If you want to disable incremental loading altogether, either remove
`doom-load-packages-incrementally-h' from `emacs-startup-hook' or set
`doom-incremental-first-idle-timer' to nil. Incremental loading does not occur
in daemon sessions (they are loaded immediately at startup).")
(defvar doom-incremental-first-idle-timer (if (daemonp) 0 2.0)
"How long (in idle seconds) until incremental loading starts.
Set this to nil to disable incremental loading.
Set this to 0 to load all incrementally deferred packages immediately at
`emacs-startup-hook'.")
(defvar doom-incremental-idle-timer 0.75
"How long (in idle seconds) in between incrementally loading packages.")
(defun doom-load-packages-incrementally (packages &optional now)
"Registers PACKAGES to be loaded incrementally.
If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer'
intervals."
(let ((gc-cons-threshold most-positive-fixnum))
(if (not now)
(cl-callf append doom-incremental-packages packages)
(while packages
(let ((req (pop packages))
idle-time)
(if (featurep req)
(message "start:iloader: Already loaded %s (%d left)" req (length packages))
(condition-case-unless-debug e
(and
(or (null (setq idle-time (current-idle-time)))
(< (float-time idle-time) doom-incremental-first-idle-timer)
(not
(while-no-input
(message "start:iloader: Loading %s (%d left)" req (length packages))
;; If `default-directory' doesn't exist or is
;; unreadable, Emacs throws file errors.
(let ((default-directory user-emacs-directory)
(inhibit-message t)
(file-name-handler-alist
(list (rassq 'jka-compr-handler file-name-handler-alist))))
(require req nil t)
t))))
(push req packages))
(error
(message "Error: failed to incrementally load %S because: %s" req e)
(setq packages nil)))
(if (null packages)
(message "start:iloader: Finished!")
(run-at-time (if idle-time
doom-incremental-idle-timer
doom-incremental-first-idle-timer)
nil #'doom-load-packages-incrementally
packages t)
(setq packages nil))))))))
(defun doom-load-packages-incrementally-h ()
"Begin incrementally loading packages in `doom-incremental-packages'.
If this is a daemon session, load them all immediately instead."
(when (numberp doom-incremental-first-idle-timer)
(if (zerop doom-incremental-first-idle-timer)
(mapc #'require (cdr doom-incremental-packages))
(run-with-idle-timer doom-incremental-first-idle-timer
nil #'doom-load-packages-incrementally
(cdr doom-incremental-packages) t))))
(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h 100)
添加 :ban
关键字,不要执行 use-package
(require 'use-package)
(push :ban use-package-keywords)
(defalias 'use-package-normalize/:ban 'use-package-normalize-test)
(defalias 'use-package-handler/:ban 'use-package-handler/:unless)
(use-package benchmark-init
:ban not-debug?
:straight t
:demand t
:hook (after-init . benchmark-init/deactivate)
:bind ("<f7>" . benchmark-init/show-durations-tree)
)
;;; init.el --- init -*- lexical-binding: t no-byte-compile: t; -*-
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
(defconst sys-is-windows (memq system-type '(cygwin windows-nt ms-dos)))
(defconst sys-is-wsl2 (getenv "WSLENV"))
;; 动态库目录
(defconst my-lib-dir (expand-file-name "lib" user-emacs-directory))
(add-to-list 'load-path my-lib-dir)
;; 可执行文件目录
(defconst my-bin-dir (expand-file-name "bin" user-emacs-directory))
;; 将 my-bin-dir 加入到 PATH 中
(setenv "PATH" (concat my-bin-dir (if sys-is-windows ";" ":") (getenv "PATH")))
;; 外部配置文件目录
(defconst my-etc-dir (expand-file-name "etc" user-emacs-directory))
;; 存放代码的目录
(if sys-is-windows
(defconst my-code-dir (expand-file-name "D:/Code"))
(defconst my-code-dir (expand-file-name "~/Code")))
;; 开源代码
(defconst my-code-opensource-dir (expand-file-name "Github" my-code-dir))
;; 工作代码
(defconst my-code-work-dir (expand-file-name "Work" my-code-dir))
;; 个人项目代码
(defconst my-code-project-dir (expand-file-name "Project" my-code-dir))
;; 练习代码或实例代码,这里面一般根据语言再分一层
(defconst my-code-demo-dir (expand-file-name "Demo" my-code-dir))
;; 数据目录
(if sys-is-windows
(defconst my-data-dir (expand-file-name "D:/Data"))
(if sys-is-wsl2
(defconst my-data-dir (expand-file-name "/mnt/d/Data"))
(defconst my-data-dir (expand-file-name "~/Data"))))
;; 笔记目录
(defconst my-notes-dir (expand-file-name "Notes" my-data-dir))
;; 博客相关目录
(defconst my-blogs-dir (expand-file-name "Blogs" my-data-dir))
(defconst my-blogs-org-dir (expand-file-name "org" my-blogs-dir))
(defconst my-blogs-image-dir (expand-file-name "images" my-blogs-dir))
;; Org 相关目录
(if sys-is-windows
(defconst my-org-dir (expand-file-name "D:/Org"))
(if sys-is-wsl2
(defconst my-org-dir (expand-file-name "/mnt/d/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))
(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))))
(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)
)))
(defun my/clear-messages-buffer ()
"Clear *Messages* buffer."
(interactive)
(let ((inhibit-read-only t))
(with-current-buffer "*Messages*"
(erase-buffer))))
(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"))))))
(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))
))))
(setq user-full-name "xhcoding"
user-mail-address "[email protected]")
(setq process-adaptive-read-buffering nil)
(setq read-process-output-max (* 1024 1024))
默认使用 utf-8 ,在 windows 文件名编码使用 gbk ,不然打不开中文文件
(prefer-coding-system 'utf-8)
(when sys-is-windows
(setq file-name-coding-system 'gbk))
(setq frame-title-format "Emacs")
(setq inhibit-startup-message t)
scratch 为 fundaemental-mode
(setq initial-major-mode 'fundamental-mode)
scratch buffer 内容为空
(setq initial-scratch-message nil)
(setq use-short-answers t)
(setq ring-bell-function 'ignore)
(blink-cursor-mode -1)
For information about GNU Emacs…
(advice-add #'display-startup-echo-area-message :override #'ignore)
(setq-default tab-width 4)
(setq-default indent-tabs-mode nil)
(setq-default fill-column 120)
(column-number-mode +1)
(setq-default delete-by-moving-to-trash t)
(global-hl-line-mode +1)
(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
ref: https://github.com/manateelazycat/auto-save
(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)
)
(use-package rime
:straight t
:defer t
:bind ("C-j" . rime-force-enable)
:init
(when sys-is-windows
(setq rime--module-path
(expand-file-name (concat "librime-emacs" module-file-suffix) my-lib-dir))
(setq rime-share-data-dir (expand-file-name "rime-data" my-etc-dir)))
(defun my-*require-rime(&rest _)
"Require rime when toggle-input-method."
(unless (featurep 'rime)
(require 'rime)))
(advice-add 'toggle-input-method :before #'my-*require-rime)
:custom
(default-input-method "rime")
(rime-user-data-dir (expand-file-name "rime-user" my-etc-dir))
:config
(setq
rime-disable-predicates '(rime-predicate-after-alphabet-char-p
rime-predicate-prog-in-code-p))
(if (display-graphic-p)
(setq rime-show-candidate 'posframe)
(setq rime-show-candidate 'minibuffer)))
(defun my-font-installed-p (font-name)
"Check if font with FONT-NAME is available."
(find-font (font-spec :name font-name)))
(defun my-setup-fonts ()
"Setup fonts."
(when (display-graphic-p)
;; Set default font
(cl-loop for font in '("CaskaydiaCove NFP" "Fira Code" "Jetbrains Mono"
"SF Mono" "Hack" "Source Code Pro" "Menlo"
"Monaco" "DejaVu Sans Mono" "Consolas")
when (my-font-installed-p font)
return (set-face-attribute 'default nil
:family font
:height 120))
;; Specify font for all unicode characters
(cl-loop for font in '("Segoe UI Emoji" "Apple Symbols" "Symbola" "Symbol")
when (my-font-installed-p font)
return (set-fontset-font t 'symbol (font-spec :family font) nil 'prepend))
;; Emoji
(cl-loop for font in '("Segoe UI Emoji" "Noto Color Emoji" "Apple Color Emoji")
when (my-font-installed-p font)
return (set-fontset-font t
(if (< emacs-major-version 28)'symbol 'emoji)
(font-spec :family font) nil 'prepend))
;; Specify font for Chinese characters
(cl-loop for font in '("微软雅黑" "WenQuanYi Micro Hei Mono")
when (my-font-installed-p font)
return (set-fontset-font t 'han (font-spec :family font)))))
(my-setup-fonts)
(add-hook 'window-setup-hook #'my-setup-fonts)
(add-hook 'server-after-make-frame-hook #'my-setup-fonts)
(ignore-errors
(load-theme 'modus-operandi-tinted :no-confirm))
(use-package nerd-icons
:straight t
:init
(cl-loop for font in '("CaskaydiaCove NFP" "Symbols Nerd Font Mono")
when (my-font-installed-p font)
return (setq nerd-icons-font-family font)))
(use-package ligature
:ban minimal?
:straight t
:defer t
:hook prog-mode
:config
;; Enable all Cascadia Code ligatures in programming modes
(ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\\\" "://")))
(use-package doom-modeline
:straight t
:hook (after-init . doom-modeline-mode))
(use-package hydra
:straight t)
(use-package pretty-hydra
:straight t
:after hydra
:init
(cl-defun pretty-hydra-title (title &optional icon-type icon-name
&key face height v-adjust)
"Add an icon in the hydra title."
(let ((face (or face `(:foreground ,(face-background 'highlight))))
(height (or height 1.0))
(v-adjust (or v-adjust 0.0)))
(concat
(when (and icon-type icon-name)
(let ((f (intern (format "all-the-icons-%s" icon-type))))
(when (fboundp f)
(concat
(apply f (list icon-name :face face :height height :v-adjust v-adjust))
" "))))
(propertize title 'face face)))))
(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))
(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))
)
(use-package autorevert
:config
(global-auto-revert-mode +1))
(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))))
(use-package vundo
:straight t
:bind ("C-x u" . vundo))
(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))
(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))
)
ref:https://github.com/manateelazycat/toggle-one-window
(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)
(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)
(use-package separedit
:straight t
:bind ("C-c '" . separedit))
(use-package fancy-compilation
:straight t
:after compile
:config
(setq fancy-compilation-override-colors nil)
(fancy-compilation-mode +1))
(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)
)
(use-package em-hist
:defer t
:custom
(eshell-history-size 10240)
(eshell-hist-ignoredups t)
(eshell-save-history-on-exit t))
(use-package esh-mode
:bind (:map eshell-mode-map
("C-r" . consult-history)))
(use-package eshell-git-prompt
:straight t
:after esh-mode
:config
(eshell-git-prompt-use-theme 'powerline))
(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))))))
)
(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))))
(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)))
(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-code-service)))
(put ',intern-pre 'function-documentation
(format "Enable lsp-bridge-mode in the buffer of org source block (%s)."
(upcase ,lang)))
(if (fboundp ',edit-pre)
(advice-add ',edit-pre :after ',intern-pre)
(progn
(defun ,edit-pre (info)
(,intern-pre info))
(put ',edit-pre 'function-documentation
(format "Prepare local buffer environment for org source block (%s)."
(upcase ,lang))))))))
(with-eval-after-load 'org
(dolist (lang '("c" "cpp" "python" "rust"))
(eval `(my-lsp-org-babel-enable ,lang))))
:config
(push '(scss-mode . "vscode-css-language-server") lsp-bridge-single-lang-server-mode-list)
(push '(json-ts-mode . "vscode-json-language-server") lsp-bridge-single-lang-server-mode-list)
(setq lsp-bridge-user-langserver-dir my-etc-dir
lsp-bridge-user-multiserver-dir my-etc-dir)
(setq acm-enable-tabnine nil
acm-enable-quick-access t
lsp-bridge-enable-hover-diagnostic t)
)
(use-package popon
:straight t
:defer t)
(use-package acm-terminal
:defer t
:straight (acm-terminal :type git :host github :repo "twlz0ne/acm-terminal")
:init
(unless (display-graphic-p)
(with-eval-after-load 'acm
(require 'acm-terminal))))
(use-package company
:straight t
:defer t
:bind (:map company-active-map
("TAB" . company-complete-selection)))
(use-package eglot-booster
:straight (eglot-booster :type git :host github :repo "jdtsmith/eglot-booster")
:after eglot
:config (eglot-booster-mode))
(defun my-enable-code-service ()
(require 'yasnippet)
(yas-minor-mode)
(if (getenv "EMACS_USE_EGLOT")
(progn
(company-mode)
(eglot-ensure)
)
(require 'lsp-bridge)
(lsp-bridge-mode)))
(use-package highlight-parentheses
:straight t
:hook (prog-mode . highlight-parentheses-mode))
(use-package evil-nerd-commenter
:straight t
:bind ("C-/" . evilnc-comment-or-uncomment-lines))
(use-package apheleia
:straight t
:defer t)
(use-package magit
:straight (magit :type git :host github :repo "magit/magit"
:files ("lisp/magit"
"lisp/magit*.el"
"lisp/git-rebase.el"
"lisp/git-commit.el"
"docs/magit.texi"
"docs/AUTHORS.md"
"LICENSE"
"Documentation/magit.texi" ; temporarily for stable
"Documentation/AUTHORS.md" ; temporarily for stable
))
:bind ("C-x g" . magit-status)
:config
(when sys-is-windows
(setenv "GIT_ASKPASS" "git-gui--askpass")))
(use-package ssh-agency
:straight t
:after magit)
(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)))
(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)))
(use-package visual-regexp
:straight t
:defer t)
(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)
)
(setq major-mode-remap-alist
'((c-mode . c-ts-mode)
(c++-mode . c++-ts-mode)
(c-or-c++-mode . c-or-c++-ts-mode)
(cmake-mode . cmake-ts-mode)
(conf-toml-mode . toml-ts-mode)
(csharp-mode . csharp-ts-mode)
(css-mode . css-ts-mode)
(dockerfile-mode . dockerfile-ts-mode)
(go-mode . go-ts-mode)
(java-mode . java-ts-mode)
(json-mode . json-ts-mode)
(js-json-mode . json-ts-mode)
(javascript-mode . js-ts-mode)
(python-mode . python-ts-mode)
(sh-mode . bash-ts-mode)))
(setq treesit-language-source-alist
'((bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
(c . ("https://github.com/tree-sitter/tree-sitter-c"))
(cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
(css . ("https://github.com/tree-sitter/tree-sitter-css"))
(cmake . ("https://github.com/uyha/tree-sitter-cmake"))
(csharp . ("https://github.com/tree-sitter/tree-sitter-c-sharp.git"))
(dockerfile . ("https://github.com/camdencheek/tree-sitter-dockerfile"))
(elisp . ("https://github.com/Wilfred/tree-sitter-elisp"))
(go . ("https://github.com/tree-sitter/tree-sitter-go"))
(gomod . ("https://github.com/camdencheek/tree-sitter-go-mod.git"))
(html . ("https://github.com/tree-sitter/tree-sitter-html"))
(java . ("https://github.com/tree-sitter/tree-sitter-java.git"))
(javascript . ("https://github.com/tree-sitter/tree-sitter-javascript"))
(json . ("https://github.com/tree-sitter/tree-sitter-json"))
(lua . ("https://github.com/Azganoth/tree-sitter-lua"))
(make . ("https://github.com/alemuller/tree-sitter-make"))
(markdown . ("https://github.com/MDeiml/tree-sitter-markdown" nil "tree-sitter-markdown/src"))
(ocaml . ("https://github.com/tree-sitter/tree-sitter-ocaml" nil "ocaml/src"))
(org . ("https://github.com/milisims/tree-sitter-org"))
(python . ("https://github.com/tree-sitter/tree-sitter-python"))
(php . ("https://github.com/tree-sitter/tree-sitter-php"))
(typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src"))
(tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "tsx/src"))
(ruby . ("https://github.com/tree-sitter/tree-sitter-ruby"))
(rust . ("https://github.com/tree-sitter/tree-sitter-rust"))
(sql . ("https://github.com/m-novikov/tree-sitter-sql"))
(vue . ("https://github.com/merico-dev/tree-sitter-vue"))
(yaml . ("https://github.com/ikatyang/tree-sitter-yaml"))
(toml . ("https://github.com/tree-sitter/tree-sitter-toml"))
(zig . ("https://github.com/GrayJack/tree-sitter-zig"))))
(defun my-treesit-install-langs (langs)
""
(dolist (lang langs)
(let* ((out-dir (locate-user-emacs-file "tree-sitter"))
(soext (car dynamic-library-suffixes))
(lib-name (concat "libtree-sitter-" (symbol-name lang) soext))
(lib-path (expand-file-name lib-name out-dir)))
(unless (file-exists-p lib-path)
(treesit-install-language-grammar lang)))))
(unless sys-is-windows
(my-treesit-install-langs '(bash c cpp cmake dockerfile elisp json python rust yaml)))
(use-package elisp-mode
:hook (emacs-lisp-mode . my-enable-elisp-dev)
:config
(defun my-enable-elisp-dev ()
(my-enable-code-service)
(treesit-parser-create 'elisp)))
(use-package cmake-ts-mode
:hook (cmake-ts-mode . my-enable-code-service))
(use-package c-ts-mode
:hook ((c-ts-mode c++-ts-mode) . my-enable-code-service)
:custom
(c-ts-mode-indent-offset 4)
(c-basic-offset 4))
(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)))))
(use-package rust-ts-mode
:hook (rust-ts-mode . my-enable-code-service))
(use-package python
:hook (python-ts-mode . my-enable-code-service))
(use-package json-ts-mode
:hook (json-ts-mode . my-enable-code-service)
:custom
(json-ts-mode-indent-offset 2)
:config
(defun my-json-generate-language-server-json ()
(with-temp-file (expand-file-name "vscode-json-language-server.json" my-etc-dir)
(url-insert-file-contents "https://www.schemastore.org/api/json/catalog.json")
(let* ((catalog (json-parse-string (buffer-substring-no-properties (point-min) (point-max)) :object-type 'plist))
(schemas (plist-get catalog :schemas))
(exclude-pattern ".*\.\\(cff\\|cjs\\|js\\|mjs\\|toml\\|yaml\\|yml\\)$")
(json-validation ))
(mapc #'(lambda (schema)
(when-let* ((url (plist-get schema :url))
(file-match (plist-get schema :fileMatch))
(filtered-file-match (seq-filter #'(lambda (match)
(and (not (string-prefix-p "!" match))
(not (string-match-p exclude-pattern match))))
file-match)))
(add-to-list 'json-validation `((:url . ,url) (:fileMatch ., filtered-file-match)))))
schemas)
(let* ((ls '(:name "vscode-json-language-server" :languageId "json" :command ("vscode-json-language-server" "--stdio")))
(json `(:schemas ,json-validation))
(settings `(:json ,json))
(json-encoding-pretty-print t))
(plist-put ls :settings settings)
(erase-buffer)
(goto-char (point-min))
(insert (json-encode ls))))))
)
(use-package qml-ts-mode
:ban minimal?
:straight (qml-ts-mode :type git :host github :repo "xhcoding/qml-ts-mode")
:hook (qml-ts-mode . my-enable-code-service)
:custom
(qml-ts-mode-indent-offset 4))
(use-package plantuml-mode
:ban minimal?
:straight t
:defer t
:custom
(plantuml-jar-path (expand-file-name "plantuml.jar" my-lib-dir))
(plantuml-default-exec-mode 'jar))
(use-package dape
:straight t)
(use-package dash-docs
:ban minimal?
:straight t
:commands (my/dash-docs-search)
:bind (([remap apropos-documentation] . my/dash-docs-search)
("<f1> 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))))
)
(use-package shrface
:ban minimal?
:straight t
:defer t
:config
(shrface-basic)
(shrface-trial)
(shrface-default-keybindings) ; setup default keybindings
(setq shrface-href-versatile t))
(use-package eww
:ban minimal?
:defer t
:init
(add-hook 'eww-after-render-hook #'shrface-mode)
:config
(require 'shrface))
(use-package olivetti
:ban minimal?
:straight t
:defer t
:hook (prog-mode text-mode outline-mode special-mode elfeed-show-mode)
)
(use-package org
:ban minimal?
:defer t
:straight t
:hook (org-mode . my--org-prettify-symbols)
:custom-face
;; 设置Org mode标题以及每级标题行的大小
(org-document-title ((t (:height 1.75 :weight bold))))
(org-level-1 ((t (:height 1.2 :weight bold))))
(org-level-2 ((t (:height 1.15 :weight bold))))
(org-level-3 ((t (:height 1.1 :weight bold))))
(org-level-4 ((t (:height 1.05 :weight bold))))
(org-level-5 ((t (:height 1.0 :weight bold))))
(org-level-6 ((t (:height 1.0 :weight bold))))
(org-level-7 ((t (:height 1.0 :weight bold))))
(org-level-8 ((t (:height 1.0 :weight bold))))
(org-level-9 ((t (:height 1.0 :weight bold))))
;; 设置代码块用上下边线包裹
(org-block-begin-line ((t (:underline t :background unspecified))))
(org-block-end-line ((t (:overline t :underline nil :background unspecified))))
:custom
;; 标题行美化
(org-fontify-whole-heading-line t)
;; 设置标题行折叠符号
(org-ellipsis " ▾")
;; TODO标签美化
(org-fontify-todo-headline t)
;; DONE标签美化
(org-fontify-done-headline t)
;; 引用块美化
(org-fontify-quote-and-verse-blocks t)
;; 隐藏宏标记
(org-hide-macro-markers t)
;; 隐藏强调标签
(org-hide-emphasis-markers t)
;; 高亮latex语法
(org-highlight-latex-and-related '(native script entities))
;; 以UTF-8显示
(org-pretty-entities t)
;; 当启用缩进模式时自动隐藏前置星号
(org-indent-mode-turns-on-hiding-stars t)
;; 自动启用缩进
(org-startup-indented nil)
;; 根据标题栏自动缩进文本
(org-adapt-indentation nil)
;; 自动显示图片
(org-startup-with-inline-images t)
;; 默认以Overview的模式展示标题行
(org-startup-folded 'overview)
;; 允许字母列表
(org-list-allow-alphabetical t)
;; 编辑时检查是否在折叠的不可见区域
(org-fold-catch-invisible-edits 'smart)
;; 上标^下标_是否需要特殊字符包裹,这里设置需要用大括号包裹
(org-use-sub-superscripts '{})
:config
(when (my-font-installed-p "等距更纱黑体 SC")
(create-fontset-from-fontset-spec
(font-xlfd-name
(font-spec :family "等距更纱黑体 SC"
:weight 'regular
:slant 'normal
:registry "fontset-orgtable")))
(set-fontset-font "fontset-orgtable" '(#x0 . #xffff)
(font-spec :family "等距更纱黑体 SC"
:weight 'regular
:slant 'normal))
(set-face-attribute 'org-table nil :fontset "fontset-orgtable" :font "fontset-orgtable"))
(defun my--org-prettify-symbols ()
(setq prettify-symbols-alist
(mapcan (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
'(
("#+begin_src" . ?✎)
("#+end_src" . ?□)
("#+results:" . ?💻)
("#+date:" . ?📅)
("#+author:" . ?👤)
("#+title:" . ?📓)
("#+identifier:" . ?🆔)
("#+hugo_tags:" . ?📍)
("#+hugo_categories:" . ?📁)
("#+hugo_locale:" . ?🌐)
("#+hugo_draft:" . ?🚮)
("#+hugo_custom_front_matter:" . ?📝)
("#+begin_quote" . ?«)
("#+end_quote" . ?»)
)))
(setq prettify-symbols-unprettify-at-point t)
(prettify-symbols-mode 1))
;; 设置标题行之间总是有空格;列表之间根据情况自动加空格
(setq org-blank-before-new-entry '((heading . t)
(plain-list-item . auto)
)))
(use-package org-modern
:ban minimal?
:straight t
:hook (org-mode . org-modern-mode)
:config
;; 额外的行间距,0.1表示10%,1表示1px
(setq-default line-spacing 0.1)
;; 复选框美化
(setq org-modern-checkbox
'((?X . #("▢✓" 0 2 (composition ((2)))))
(?- . #("▢–" 0 2 (composition ((2)))))
(?\s . #("▢" 0 1 (composition ((1)))))))
;; 列表符号美化
(setq org-modern-list
'((?- . "•")
(?+ . "◦")
(?* . "▹")))
;; 代码块类型美化,我们使用了 `prettify-symbols-mode'
(setq org-modern-block-name nil)
;; #+关键字美化,我们使用了 `prettify-symbols-mode'
(setq org-modern-keyword nil)
;; 关闭表格美化
(setq org-modern-table nil)
)
(use-package org-appear
:ban minimal?
:straight t
:hook (org-mode . org-appear-mode)
:config
(setq org-appear-autolinks t)
(setq org-appear-autosubmarkers t)
(setq org-appear-autoentities t)
(setq org-appear-autokeywords t)
(setq org-appear-inside-latex t))
(use-package org
:ban minimal?
:defer t
:straight t
:custom
(org-directory my-org-dir)
(org-modules '(ol-wl))
(org-structure-template-alist
'(("q" . "quote\n")
("s" . "src")
("e" . "src elisp\n")
("c" . "src cpp\n")
("h" . "export html"))
))
(use-package org
:ban minimal?
:defer t
:straight t
:custom
(org-confirm-babel-evaluate nil)
(org-export-use-babel nil)
(org-src-lang-modes '(("C" . c-ts)
("C++" . c++-ts)
("asymptote" . asy)
("bash" . sh)
("beamer" . latex)
("calc" . fundamental)
("cpp" . c++-ts)
("ditaa" . artist)
("desktop" . conf-desktop)
("dot" . fundamental)
("elisp" . emacs-lisp)
("ocaml" . tuareg)
("screen" . shell-script)
("shell" . sh)
("sqlite" . sql)
("toml" . conf-toml)))
:config
(org-babel-do-load-languages 'org-babel-load-languages
'((emacs-lisp . t)
(perl . t)
(python . t)
(ruby . t)
(js . t)
(css . t)
(sass . t)
(C . t)
(java . t)
(plantuml . t)))
;; 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)
)
(use-package :org
:ban minimal?
:defer t
:bind (("C-c a" . org-agenda)
("C-c c" . org-capture))
:init
(setq my-org-gtd-dir (expand-file-name "Gtd" my-org-dir)
my-org-inbox-file (expand-file-name "inbox.org" my-org-gtd-dir)
my-org-projects-file (expand-file-name "projects.org" my-org-gtd-dir))
(defun my-org--verify-refile-target ()
"Exclude todo keywords with a done state from refile targets."
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
:custom
(org-agenda-files `(,my-org-inbox-file ,my-org-projects-file))
(org-capture-templates `(("i" "Inbox" entry
(file ,my-org-inbox-file)
,(concat "* TODO %?\n"
"/Entered on/ %U"))))
(org-todo-keywords
'((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)" "CANCELLED(c)")))
(org-refile-targets '((nil :maxlevel . 9)
(org-agenda-files :maxlevel . 9)))
(org-refile-use-outline-path t)
(org-outline-path-complete-in-steps nil)
(org-refile-allow-creating-parent-nodes 'confirm)
(org-refile-target-verify-function 'my-org--verify-refile-target)
(org-agenda-span 'day)
(org-agenda-hide-tags-regexp ".")
(org-agenda-prefix-format
'((agenda . " %i %-12:c%?-12t% s")
(todo . " ")
(tags . " %i %-12:c")
(search . " %i %-12:c")))
(org-agenda-custom-commands
'(("g" "Get Things Done (GTD)"
((agenda "" ((org-deadline-warning-days 0)))
(todo "NEXT"
((org-agenda-prefix-format " %i %-12:c ")
(org-agenda-overriding-header "\nTasks\n")))
(tags-todo "inbox"
((org-agenda-prefix-format " %?-12t% s")
(org-agenda-overriding-header "\nInbox\n")))))))
)
(use-package org
:ban minimal?
:straight t
:defer t
:hook (org-mode . my--set-org-html-head-extra)
:custom
(org-export-with-broken-links t)
:config
(defun my--set-org-html-head-extra ()
"Set org html head extra"
(let ((path (expand-file-name "custom-head.html" my-etc-dir)))
(when (file-exists-p path)
(setq org-html-head-extra (with-temp-buffer
(insert-file-contents path)
(buffer-string))))))
)
(use-package htmlize
:ban minimal?
:straight t
:after org)
(use-package toc-org
:ban minimal?
:straight t
:commands (toc-org-insert-toc)
:custom
(toc-org-max-depth 3))
(use-package org-contrib
:ban minimal?
:straight t
:after org)
(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)))
(use-package denote
:ban minimal?
:straight t
:custom
(denote-directory my-notes-dir)
:config
(defun denote-blog ()
"Create blog for publish."
(declare (interactive-only t))
(interactive)
(let ((denote-directory my-blogs-org-dir)
(denote-prompts '(title))
(denote-org-front-matter
"#+title: %1$s
#+date: %2$s
#+author: xhcoding
#+identifier: %4$s
#+hugo_locale: zh
#+hugo_tags: %3$s
#+hugo_categories: %3$s
#+hugo_draft: false
"
))
(call-interactively #'denote-open-or-create)))
(defun denote-blog-image-insert (file)
"Insert image file to blog."
(declare (interactive-only t))
(interactive "fImage file: ")
(let* ((blog-file-title (denote-retrieve-filename-title (buffer-file-name)))
(blog-image-dir (concat (expand-file-name blog-file-title my-blogs-image-dir) "/"))
(blog-image-ext (concat "." (file-name-extension file)))
(blog-default-image-title (concat blog-file-title (format-time-string "-%H%M%S")))
(blog-image-title (read-string "Image title: " "" nil blog-default-image-title))
(blog-image-new-name (denote-format-file-name blog-image-dir (denote-get-identifier) nil blog-image-title blog-image-ext nil)))
(unless (file-directory-p blog-image-dir)
(make-directory blog-image-dir t))
(copy-file file blog-image-new-name)
(org-insert-link "" (concat "file:" blog-image-new-name) "")
))
)
(if sys-is-windows
(defconst my-hugo-root-dir (expand-file-name "D:/Blog"))
(if sys-is-wsl2
(defconst my-hugo-root-dir (expand-file-name "/mnt/d/Blog"))
(defconst my-hugo-root-dir (expand-file-name "~/Blog"))))
(defconst my-hugo-image-url "https://images.xhcoding.cn/blog")
(defconst my-hugo-post-url "https://xhcoding.cn/post/")
(use-package ox-hugo
:ban minimal?
:straight t
:after org
:custom
(org-hugo-base-dir my-hugo-root-dir)
(org-hugo-section "post")
(org-hugo-default-section-directory "post")
;; (org-hugo-auto-set-lastmod t)
:config
(defun my-hugo--blogs-image-path-p (raw-path)
(let ((file-path (expand-file-name raw-path)))
(equal (string-match-p (regexp-quote (expand-file-name my-blogs-image-dir)) file-path) 0)))
;; D:/Data/Blogs/images/1.png ==> https://images.xhcoding.cn/blog/1.png
(defun my-hugo--image-path-to-url (raw-path)
(let ((file-path (expand-file-name raw-path)))
(concat my-hugo-image-url (string-trim-left file-path my-blogs-image-dir))))
;; D:/Data/Blogs/images/1.png ==> https://images.xhcoding.cn/blog/1.png
(defun my-hugo*convert-path-to-url (args)
(let* ((link (nth 0 args))
(desc (nth 1 args))
(info (nth 2 args))
(type (org-element-property :type link))
(raw-path (org-element-property :path link)))
(cond
((and (string-equal type "file") (my-hugo--blogs-image-path-p raw-path))
(progn
(let* ((image-url (my-hugo--image-path-to-url raw-path))
(new-link (org-element-put-property link :path image-url )))
`(,new-link ,desc ,info))))
(t `(,link ,desc ,info))
)))
(advice-add #'org-hugo-link :filter-args #'my-hugo*convert-path-to-url)
(defun my/hugo-export-all-blogs ()
"Export all blogs."
(interactive)
(dolist (file (directory-files my-blogs-org-dir))
(when (string-equal "org" (file-name-extension file))
(with-temp-buffer
(find-file (expand-file-name file my-blogs-org-dir))
(org-hugo-export-to-md)))))
)
(use-package easy-hugo
:ban minimal?
:straight t
:defer t
:config
(setq easy-hugo-basedir (expand-file-name my-hugo-root-dir)
easy-hugo-postdir (expand-file-name my-blogs-org-dir)
easy-hugo-org-header t
easy-hugo-github-deploy-script "deploy.bat"))
(use-package pangu-spacing
:ban minimal?
:straight t
:after org
:hook (org-mode . pangu-spacing-mode)
:custom
(pangu-spacing-real-insert-separtor t))
(use-package org-download
:disabled t
:ban minimal?
:straight t
:after org
:config
(when sys-is-windows
(setq org-download-screenshot-method "irfanview /capture=4 /convert=\"%s\""
org-download-display-inline-images 'posframe
org-download-abbreviate-filename-function 'expand-file-name))
(setq-default org-download-image-dir my-blog-img-dir
org-download-heading-lvl nil)
;; 截图的名称不要总是 screenshot
(defun my/org-download-screenshot ()
"Capture screenshot and insert the resulting file.
The screenshot tool is determined by `org-download-screenshot-method'."
(interactive)
(let* ((screenshot-dir (file-name-directory org-download-screenshot-file))
(org-file-path (buffer-file-name))
(org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path)))
(new-screenshot-name (concat org-file-name ".png"))
(new-screenshot-path (expand-file-name new-screenshot-name screenshot-dir)))
(when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path))
(setq new-screenshot-name
(substring new-screenshot-name (+ 1 (string-match-p "-" new-screenshot-name)))
new-screenshot-path
(expand-file-name new-screenshot-name screenshot-dir)))
(make-directory screenshot-dir t)
(if (functionp org-download-screenshot-method)
(funcall org-download-screenshot-method
org-download-screenshot-file)
(shell-command-to-string
(format org-download-screenshot-method
org-download-screenshot-file)))
(when (file-exists-p org-download-screenshot-file)
(rename-file org-download-screenshot-file new-screenshot-path)
(org-download-image new-screenshot-path)
(delete-file new-screenshot-path))))
(defun my/org-download-clipboard()
"Download from clipboard"
(interactive)
(let ((org-download-screenshot-method "irfanview /clippaste /convert=\"%s\""))
(my/org-download-screenshot)))
;; 将图片保存到当前 buffer 名称目录下
;; ref: https://github.com/abo-abo/org-download/issues/195
(defun my-org-download-method (link)
(let* ((filename
(file-name-nondirectory
(car (url-path-and-query
(url-generic-parse-url link)))))
(org-file-path (buffer-file-name))
(org-file-name (file-name-sans-extension (file-name-nondirectory org-file-path)))
(dirname (expand-file-name org-file-name my-blog-img-dir)))
(when (and (featurep 'org-roam) (string-prefix-p org-roam-directory org-file-path))
(setq dirname (expand-file-name
(substring org-file-name (+ 1 (string-match-p "-" org-file-name)))
my-blog-img-dir)))
(make-directory dirname t)
(expand-file-name (funcall org-download-file-format-function filename) dirname)))
(setq org-download-method 'my-org-download-method))
(use-package eaf
:ban minimal?
:disabled t
:straight (emacs-application-framework :type git :host github :repo "emacs-eaf/emacs-application-framework"
:files ("*")
:build nil)
:defer t
:commands (eaf-open eaf-open-browser eaf-open-this-buffer eaf-open-pdf-from-history eaf-open-cloud-music)
:init
;; 手动添加到 load-path
(add-to-list 'load-path (straight--repos-dir "emacs-application-framework"))
:config
(setq eaf-proxy-type "http")
(setq eaf-proxy-host "127.0.0.1")
(setq eaf-proxy-port "7890")
(setq eaf-webengine-default-zoom 2.5)
(when sys-is-windows
(setq eaf-chrome-bookmark-file (expand-file-name "~/AppData/Local/Microsoft/Edge/User Data/Default/Bookmarks"))
(defun my-eaf--enable-python-utf8-mode (environment)
(append (list "PYTHONUTF8=1") environment))
(advice-add 'eaf--build-process-environment :filter-return #'my-eaf--enable-python-utf8-mode))
(require 'eaf-browser)
(require 'eaf-pdf-viewer)
(require 'eaf-org-previewer)
(require 'eaf-markdown-previewer)
(require 'eaf-music-player))
(use-package popweb
:disabled t
:ban minimal?
:straight (popweb :type git :host github :repo "manateelazycat/popweb" :build nil)
:commands (popweb-dict-eudic-input popweb-import-browser-cookies)
:bind ("<f1> 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';"
))
)
(use-package websocket
:disabled t
:ban minimal?
:straight t
:defer t)
(use-package websocket-bridge
:disabled t
:ban minimal?
:straight (websocket-bridge :type git :host github :repo "ginqi7/websocket-bridge")
:defer t)
(use-package dictionary-overlay
:disabled t
:ban minimal?
:straight (dictionary-overlay :type git :host github :repo "ginqi7/dictionary-overlay"
:build nil)
:defer t
:init
(add-to-list 'load-path (straight--repos-dir "dictionary-overlay"))
:custom
(dictionary-overlay-python "python"))
Wanderlust + offlineimap3 + mu
(use-package wl
:disabled t
:ban minimal?
:straight (wanderlust)
:defer t
:hook ((wl . meow-motion-mode))
:bind (:map wl-folder-mode-map
(("q" . wl-folder-suspend)))
:init
(setq wl-init-file (expand-file-name "wl.el" my-private-dir)
wl-folders-file (expand-file-name "folders.wl" my-private-dir)
wl-address-file (expand-file-name "address.wl" my-private-dir))
:config
(if (boundp 'mail-user-agent)
(setq mail-user-agent 'wl-user-agent))
(if (fboundp 'define-mail-user-agent)
(define-mail-user-agent
'wl-user-agent
'wl-user-agent-compose
'wl-draft-send
'wl-draft-kill
'mail-send-hook))
(setq wl-quicksearch-folder "[]")
(setq wl-message-ignored-field-list
'(".")
wl-message-visible-field-list
'("^\\(To\\|Cc\\):"
"^Subject:"
"^\\(From\\|Reply-To\\):"
"^\\(Posted\\|Date\\):"
"^Organization:"
"^X-\\(Face\\(-[0-9]+\\)?\\|Weather\\|Fortune\\|Now-Playing\\):")
wl-message-sort-field-list
(append wl-message-sort-field-list
'("^Reply-To" "^Posted" "^Date" "^Organization")))
;; windows 上 mu find 返回的路径以 /cygdrive/ 开头,我们需要自己处理一下
(defun my--elmo-search-parse-filename-list ()
(let (bol locations)
(goto-char (point-min))
(while (not (eobp))
(beginning-of-line)
(when (and elmo-search-use-drive-letter
(looking-at "^\\(/cygdrive/\\)?\\([A-Za-z]\\)\\([:|]\\)?/"))
(replace-match "/\\2:/")
(beginning-of-line))
(unless (looking-at "^file://")
(insert "file://")
(beginning-of-line))
(setq bol (point))
(end-of-line)
(setq locations (cons (buffer-substring bol (point)) locations))
(forward-line))
(nreverse locations)))
(elmo-search-register-engine
'mu-msys 'local-file
:prog "mu"
:args '("find" elmo-search-split-pattern-list "--fields" "l")
:charset 'utf-8
:parser 'my--elmo-search-parse-filename-list)
(setq elmo-search-default-engine 'mu-msys)
;; mu 的输入要用 gbk 编码,不然无法输入中文
(add-to-list 'process-coding-system-alist '("mu" utf-8 . gbk))
;; mime 附件保存目录
(setq mime-save-directory (expand-file-name "mails" my-archives-dir))
)
调用 compose-mail 前先 require wanderlust
(when full?
(defun my-*require-wanderlust (&rest _)
(require 'wl))
(advice-add 'compose-mail :before #'my-*require-wanderlust))
(use-package alert-toast
:disabled t
:ban minimal?
:straight t
:after wl
:config
(defun my--notify-new-mail-arrived (number)
(alert-toast-notify `(:title "Wanderlust" :message ,(format "你有 %s 封未读邮件" number))))
(add-hook 'wl-biff-new-mail-hook #'my--notify-new-mail-arrived)
)
(use-package elfeed
:ban minimal?
:straight t
:defer t
:config
(defun my-eaf-elfeed-open-url ()
"Display the currently selected item in an eaf buffer."
(interactive)
(let ((entry (elfeed-search-selected :ignore-region)))
(require 'elfeed-show)
(when (elfeed-entry-p entry)
;; Move to next feed item.
(elfeed-untag entry 'unread)
(elfeed-search-update-entry entry)
(unless elfeed-search-remain-on-entry (forward-line))
(my/toggle-one-window)
(eaf-open-browser (elfeed-entry-link entry))
)))
)
(use-package elfeed-org
:ban minimal?
:straight t
:after elfeed
:config
(elfeed-org)
(setq rmh-elfeed-org-files `(,(expand-file-name "elfeed.org" my-org-dir))))
(use-package leetcode
:ban minimal?
:straight (leetcode :type git :host github :repo "xhcoding/leetcode.el")
:defer t
:custom
(leetcode-prefer-language "cpp")
(leetcode-save-solutions t)
(leetcode-directory (expand-file-name "Project/LeetCode" my-code-dir))
:config
;; 在 leetcode 里关闭自动补全
(defun my-*after-leetcode--start-coding (problem problem-info)
(let-alist problem
(let* ((title (leetcode-problem-title problem-info))
(code-buf-name (leetcode--get-code-buffer-name title)))
(with-current-buffer (leetcode--get-code-buffer code-buf-name)
(when lsp-bridge-mode
(lsp-bridge-mode -1))))))
(advice-add 'leetcode--start-coding :after 'my-*after-leetcode--start-coding)
)
(use-package tex
:disabled t
:ban minimal?
: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")))
(use-package async-await
:ban minimal?
:straight t
:defer t)
(use-package websocket
:ban minimal?
:straight t
:defer t)
(use-package aichat
:ban minimal?
:defer t
:load-path (lambda () (expand-file-name "Project/emacs-aichat" my-code-dir))
:config
(setq aichat-bingai-proxy "localhost:7890"
aichat-openai-proxy "localhost:7890")
(aichat-toggle-debug)
(aichat-bingai-prompt-create "translator"
:input-prompt "请翻译: "
:text-format "我想让你充当翻译员,我会用任何语言与你交谈,你会检测我说的的语言,如果我说的是中文,你就翻译成英文;如果我说的不是中文,你就翻译成英文。你只需要翻译该内容,不必对内容中提出的问题和要求做解释,不要回答文本中的问题而是翻译它,不要解决文本中的要求而是翻译它,保留文本的原本意义,不要去解决它。你的回答里只需要翻译后的内容,不要有任何其它词,只能是翻译后的内容。我的第一句话是:\n%s"
:chat t
:assistant t
:replace-or-insert t)
(aichat-bingai-prompt-create "coder"
:input-prompt "代码: "
:text-format "我想让你充当计算机教授,请向我解释下面这段代码的作用:\n%s"
:chat t)
(aichat-bingai-prompt-create "refactor"
:input-prompt "代码: "
:text-format "我想让你充当计算机教授,请帮我重构下面这段代码,重构后的代码性能要更好,可读性要更高,如果必要的话,可以加一些注释。你的回答里只需要返回重构后的代码,不要有其它解释,只能是重构后的代码:\n%s"
:replace-or-insert t)
)
(use-package which-key
:straight t
:hook (after-init . which-key-mode))
(use-package meow
:straight t
:demand t
:hook (after-init . meow-global-mode)
:config
(with-eval-after-load 'rime
(dolist (p '(meow-normal-mode-p meow-motion-mode-p meow-keypad-mode-p))
(add-to-list 'rime-disable-predicates p)))
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
(setq meow-char-thing-table
'((?r . round)
(?c . curly)
(?s . string)
(?b . buffer)
(?d . defun)))
(setq meow-use-clipboard t)
(meow-leader-define-key
;; Use SPC (0-9) for digit arguments.
'("1" . meow-digit-argument)
'("2" . meow-digit-argument)
'("3" . meow-digit-argument)
'("4" . meow-digit-argument)
'("5" . meow-digit-argument)
'("6" . meow-digit-argument)
'("7" . meow-digit-argument)
'("8" . meow-digit-argument)
'("9" . meow-digit-argument)
'("0" . meow-digit-argument)
'("?" . meow-cheatsheet)
'("b" . consult-buffer)
'("r" . consult-ripgrep)
'("f" . consult-find)
'("/" . evilnc-comment-or-uncomment-lines)
'("w" . ace-window-hydra/body)
)
(meow-normal-define-key
;; char move
'("j" . meow-next)
'("J" . meow-next-expand)
'("k" . meow-prev)
'("K" . meow-prev-expand)
'("l" . meow-right)
'("L" . meow-right-expand)
'("h" . meow-left)
'("H" . meow-left-expand)
;; word move
'("e" . meow-next-word)
'("E" . meow-next-symbol)
'("b" . meow-back-word)
'("B" . meow-back-symbol)
;; line move
'("x" . meow-line)
'("X" . meow-goto-line)
;; thing move
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("[" . meow-beginning-of-thing)
'("]" . meow-end-of-thing)
'("o" . meow-block)
'("O" . meow-to-block)
;; jump
'("n" . meow-search)
'("f" . meow-find)
'("v" . meow-visit)
;; action
'("i" . meow-insert)
'("I" . meow-open-above)
'("a" . meow-append)
'("A" . meow-open-below)
'("y" . meow-save)
'("Y" . meow-sync-grab)
'("c" . meow-change)
'("r" . meow-replace)
'("R" . meow-swap-grab)
'("p" . meow-yank)
'("s" . meow-kill)
'("d" . meow-delete)
'("D" . meow-backward-delete)
'("G" . meow-grab)
'("m" . meow-join)
'("t" . meow-till)
'("u" . undo)
'("U" . meow-undo)
'("w" . meow-mark-word)
'("W" . meow-mark-symbol)
'("z" . meow-pop-selection)
;; others
'("0" . meow-expand-0)
'("9" . meow-expand-9)
'("8" . meow-expand-8)
'("7" . meow-expand-7)
'("6" . meow-expand-6)
'("5" . meow-expand-5)
'("4" . meow-expand-4)
'("3" . meow-expand-3)
'("2" . meow-expand-2)
'("1" . meow-expand-1)
'("-" . negative-argument)
'(";" . meow-reverse)
'("g" . meow-cancel-selection)
'("q" . meow-quit)
'("'" . repeat)
'("<escape>" . ignore)
)
)
(when sys-is-windows
(unless (daemonp)
(server-start)))
(ignore-errors
(load (expand-file-name "config.el" my-private-dir)))