Here is my personal configuration file for Emacs. The whole README file is a symlink to solarion-emacs.org
, and with org babel
, this file could be tangled into separate emacs-lisp files under $HOME/.emacs.d/lisp/
.
前置依赖与说明:
- 运行环境:Emacs 29+(使用了 pixel-scroll-precision-mode、treesit 等特性)
- LaTeX 预览:Ghostscript(用于 org/LaTeX 片段预览)
- vterm 构建:cmake、libtool、make(动态模块构建依赖)
- WSL 剪贴板:wl-copy、wl-paste(Wayland 环境下的复制/粘贴支持)
- 字体:Nerd Font(等宽)、Noto Serif CJK(中日韩)、Noto Color Emoji(Emoji)
软链与自动导出(tangle):本仓库的 README.org 指向 solarion-emacs.org。保存本文件时会自动 tangle 生成 lisp/ 下的模块文件;具体钩子与逻辑详见后文配置(auto tangle 的实现说明)。
- Solarion Emacs
- Installation
- early-init.el
- init-custom-vars.el
- init.el
- init-straight.el
- init-func.el
- init-basic.el
- init-general.el
- init-ui.el
- init-scroll.el
- init-fonts.el
- init-edit.el
- init-treesit.el
- init-hydra.el
- init-map.el
- init-vertico.el
- init-corfu.el
- init-tempel.el
- init-magit.el
- init-ibuffer.el
- init-midnight.el
- init-tramp.el
- init-org.el
- init-denote.el
- init-latex.el
- init-dired.el
- init-dashboard.el
- init-vterm.el
- init-persp.el
- init-im.el
- init-wsl.el
Clone the repository
git clone [email protected]:wang1zhen/solarion-emacs ~/.emacs.d
或使用 HTTPS 与浅克隆(首次拉取更快):
git clone --depth=1 https://github.com/wang1zhen/solarion-emacs ~/.emacs.d
系统依赖(按需安装)
- Emacs 29+(建议 GUI 版本以使用 posframe/字体配置)
- Ghostscript(LaTeX 预览与导出辅助)
- cmake、libtool、make(用于构建 vterm 动态模块)
- wl-copy、wl-paste(WSL/Wayland 剪贴板集成)
- 字体:Nerd Font、Noto Serif CJK、Noto Color Emoji
Tangle the org file
emacs --batch --eval "(progn (require 'org) (let ((org-confirm-babel-evaluate nil)) (org-babel-tangle-file \"~/.emacs.d/solarion-emacs.org\")))"
Launch Emacs and have fun!
;;; early-init.el --- early-init.el is run before package and UI initialization happens -*- lexical-binding: t -*-
;;; Code:
;; Defer garbage collection further back in the startup process
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.5)
;; Package initialize occurs automatically, before `user-init-file' is
;; loaded, but after `early-init-file'. We handle package
;; initialization, so we must prevent Emacs from doing it early!
(setq package-enable-at-startup nil)
;; Inhibit resizing frame
(setq frame-inhibit-implied-resize t)
;; Faster to disable these here (before they've been initialized)
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
(setq inhibit-startup-screen t)
(tooltip-mode -1)
;; 启动优化:临时禁用 file-name-handler 以减少 I/O 开销,启动后恢复
(defvar solarion--file-name-handler-alist-backup file-name-handler-alist)
(setq file-name-handler-alist nil)
(add-hook 'emacs-startup-hook
(lambda ()
(setq file-name-handler-alist solarion--file-name-handler-alist-backup)
(makunbound 'solarion--file-name-handler-alist-backup)))
提示:字体可通过命令定制:`M-x customize-group RET solarion-fonts RET`。 若系统缺少指定字体,本配置中的字体设置由 init-fonts.el 处理;可根据需要自行调整字体族与字号。
;;; init-custom-vars.el --- Customizable variables for Solarion -*- lexical-binding: t -*-
;;; Code:
(defgroup solarion-fonts nil
"Font configuration for Solarion Emacs."
:group 'faces)
(defcustom solarion-font-default "CaskaydiaCove Nerd Font Mono"
"Default monospace font used in Emacs (e.g., for code, modeline)."
:type 'string
:group 'solarion-fonts)
(defcustom solarion-font-variable "CaskaydiaCove Nerd Font"
"Variable-pitch font for UI elements (if used)."
:type 'string
:group 'solarion-fonts)
(defcustom solarion-font-cjk "Noto Serif CJK SC"
"CJK font for displaying Chinese/Japanese/Korean."
:type 'string
:group 'solarion-fonts)
(defcustom solarion-font-emoji "Noto Color Emoji"
"Font for emoji symbols."
:type 'string
:group 'solarion-fonts)
(defcustom solarion-font-size 140
"Font size (in 1/10 pt) for Emacs default font."
:type 'integer
:group 'solarion-fonts)
(provide 'init-custom-vars)
;;; init.el --- Load the full configuration -*- lexical-binding: t -*-
;;; Code:
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
;; custom
(require 'init-custom-vars)
;; Ensure custom.el exists, or copy from example
(let ((custom-file-path (expand-file-name "custom.el" user-emacs-directory))
(example-path (expand-file-name "custom-example.el" user-emacs-directory)))
(unless (file-exists-p custom-file-path)
(when (file-exists-p example-path)
(copy-file example-path custom-file-path)))
(setq custom-file custom-file-path)
(when (file-exists-p custom-file)
(load custom-file)))
;; Adjust garbage collection thresholds during startup, and thereafter
(let ((normal-gc-cons-threshold (* 20 1024 1024))
(init-gc-cons-threshold (* 128 1024 1024)))
(setq gc-cons-threshold init-gc-cons-threshold)
(add-hook 'emacs-startup-hook
(lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))
;; Always load newest byte code
(setq load-prefer-newer t)
;; Packages & modules(加载时抑制重绘/消息,启动后恢复,减少闪烁与噪音)
(let ((inhibit-redisplay t)
(inhibit-message t))
(require 'init-straight)
;; Useful functions defined
(require 'init-func)
;; Preferences
(require 'init-basic)
(require 'init-general)
(require 'init-ui)
(require 'init-scroll)
(require 'init-fonts)
(require 'init-edit)
(require 'init-treesit)
;; Keybindings
(require 'init-hydra)
(require 'init-map)
(require 'init-vertico)
(require 'init-corfu)
(require 'init-tempel)
(require 'init-magit)
(require 'init-ibuffer)
(require 'init-midnight)
(require 'init-tramp)
(require 'init-org)
(require 'init-denote)
(require 'init-latex)
(require 'init-dired)
(require 'init-dashboard)
(require 'init-vterm)
(require 'init-persp)
(require 'init-im)
;; WSL specific setting
(when (and (eq system-type 'gnu/linux) (getenv "WSLENV"))
(require 'init-wsl)))
(add-hook 'emacs-startup-hook
(lambda ()
(setq inhibit-redisplay nil
inhibit-message nil)
(redisplay)))
;;; init-straight.el --- Initialize package configurations -*- lexical-binding: t -*-
;;; Code:
;; Set timeout to avoid blocking on bad connections
(setq url-queue-timeout 30)
;; 使用文件监视器检测包修改(straight.el 的 watch-files 模式)。
;; 需在 straight 引导之前设置;相比启动时全量 find 更高效,
;; 但会消耗一定的文件监视资源。
;; NOTE: 需要 `watchexec` 可执行文件;若未安装,会在启动时出现
;; “Cannot start filesystem watcher without 'watchexec' installed” 警告。
(when (executable-find "watchexec")
(setq straight-check-for-modifications '(watch-files)))
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(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)))
(setq straight-use-package-by-default t)
(setq straight-vc-git-default-protocol 'https)
(setq straight-vc-git-default-clone-depth 1)
(load bootstrap-file nil 'nomessage))
;; Should set before loading `use-package'
(setq use-package-expand-minimally t)
(setq use-package-enable-imenu-support t)
;; Install use-package with straight
(straight-use-package 'use-package)
(require 'use-package)
;; Optional:profile use-package loading time
;; M-x use-package-report
(setq use-package-compute-statistics t)
;; Native compile
;; Log warnings but not pop up the *Warnings* buffer
(setq native-comp-async-report-warnings-errors 'silent)
;; Required by `use-package'
(use-package diminish)
(use-package bind-key)
(provide 'init-straight)
;;; init-func.el --- Useful functions are defined here -*- lexical-binding: t -*-
;;; Code:
;; Font
(defun font-installed-p (font-name)
"Check if font with FONT-NAME is available."
(find-font (font-spec :name font-name)))
;; Auto tangle babel file
(defun org-babel-auto-tangle ()
(when (and (eq major-mode 'org-mode)
(string-equal (buffer-name) "solarion-emacs.org"))
(org-babel-tangle)))
;; Define split-window-below-and-focus and split-window-right-and-focus
(defun split-window-below-and-focus ()
"Split the window vertically and focus the new window."
(interactive)
(split-window-below)
(windmove-down))
(defun split-window-right-and-focus ()
"Split the window horizontally and focus the new window."
(interactive)
(split-window-right)
(windmove-right))
(defun solarion/org-mode-setup ()
(auto-fill-mode 0)
(visual-line-mode 1)
(adaptive-wrap-prefix-mode 1)
;; (electric-pair-local-mode -1)
)
;; Ask for the filename before pasting an image
;; filename should end with ".png/.jpg/.svg"
(defun solarion/org-download-paste-clipboard-wsl ()
"Save clipboard image to a subdirectory named after current Org file, and insert link."
(interactive)
(unless buffer-file-name
(user-error "Buffer is not visiting a file"))
(let* ((powershell (executable-find "powershell.exe"))
(file-base-name (format-time-string "image-%Y%m%d_%H%M%S.png"))
(file-name (read-string (format "Filename [%s]: " file-base-name) nil nil file-base-name))
(org-file-dir (file-name-directory buffer-file-name))
(org-base-name (file-name-base buffer-file-name))
(image-dir (expand-file-name (concat org-base-name "/") org-file-dir))
(file-path-wsl (expand-file-name file-name image-dir)))
(unless powershell
(user-error "未找到 powershell.exe:该功能仅在 WSL/Windows 环境可用"))
;; Step 1: save to Windows temp
(shell-command
(format "%s -command \"(Get-Clipboard -Format Image).Save(\\\"C:/Users/Public/%s\\\")\""
powershell file-name))
;; Step 2: move into local folder
(make-directory image-dir t)
(rename-file (concat "/mnt/c/Users/Public/" file-name) file-path-wsl t)
;; Step 3: insert link
(let ((relative-path (file-relative-name file-path-wsl org-file-dir)))
(insert (format "#+ATTR_LATEX: :width \\linewidth\n#+ATTR_ORG: :width 400\n[[file:%s]]\n" relative-path))
(org-display-inline-images))))
;; dashboard
(defun solarion-edit-config (&rest _)
(interactive)
(find-file (concat user-emacs-directory "solarion-emacs.org")))
;; buffer
(defun solarion-new-buffer nil
(interactive)
(let ((buffer (generate-new-buffer "*new*")))
(set-window-buffer nil buffer)
(with-current-buffer buffer
(funcall (default-value 'major-mode)))))
;; Delete file and buffer
(defun delete-file-and-buffer ()
"Kill the current buffer and deletes the file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
(if filename
(if (y-or-n-p (concat "Do you really want to delete file " filename " ?"))
(progn
(delete-file filename)
(message "Deleted file %s." filename)
(kill-buffer)))
(message "Not a file visiting buffer!"))))
(defun flash-mode-line ()
(invert-face 'mode-line)
(run-with-timer 0.1 nil #'invert-face 'mode-line))
(defun copy-line (arg)
"Copy lines (as many as prefix argument) in the kill ring.
Ease of use features:
- Move to start of next line.
- Appends the copy on sequential calls.
- Use newline as last char even on the last line of the buffer.
- If region is active, copy its lines."
(interactive "p")
(let ((beg (line-beginning-position))
(end (line-end-position arg)))
(when mark-active
(if (> (point) (mark))
(setq beg (save-excursion (goto-char (mark)) (line-beginning-position)))
(setq end (save-excursion (goto-char (mark)) (line-end-position)))))
(if (eq last-command 'copy-line)
(kill-append (buffer-substring beg end) (< end beg))
(kill-ring-save beg end)))
(kill-append "\n" nil)
(beginning-of-line (or (and arg (1+ arg)) 2))
(if (and arg (not (= 1 arg))) (message "%d lines copied" arg)))
(defun solarion/git-add-commit-push ()
"Simple commit current git project and push to its upstream."
(interactive)
(when (and buffer-file-name
(buffer-modified-p))
(save-buffer)) ;; save it first if modified.
(magit-diff-unstaged)
(when (yes-or-no-p "Do you really want to commit everything?")
(magit-stage-modified t) ;; stage modified and untracked
(magit-diff-staged)
(let ((msg (read-string "Commit Message: ")))
(when (= 0 (length msg))
(setq msg (format-time-string "commit by magit in emacs@%Y-%m-%d %H:%M:%S"
(current-time))))
(magit-call-git "commit" "-m" msg)
(magit-push-current-to-upstream nil)
(message "now do async push to %s" (magit-get "remote" "origin" "url"))))
(magit-mode-bury-buffer))
(provide 'init-func)
;;; init-basic.el --- Default configurations -*- lexical-binding: t -*-
;;; Code:
;; Increase how much is read from processes in a single chunk (default is 4kb)
(setq read-process-output-max #x10000) ; 64kb
;; Garbage Collector Magic Hack
(use-package gcmh
:diminish
:init
(setq gcmh-idle-delay 5
gcmh-high-cons-threshold #x1000000) ; 16MB
:hook (after-init . gcmh-mode))
;; Encoding
;; UTF-8 as the default coding system
(when (fboundp 'set-charset-priority)
(set-charset-priority 'unicode))
;; Explicitly set the prefered coding systems to avoid annoying prompt
;; from emacs (especially on Microsoft Windows)
(prefer-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
(set-language-environment 'utf-8)
(set-default-coding-systems 'utf-8)
(set-buffer-file-coding-system 'utf-8)
(set-clipboard-coding-system 'utf-8)
(set-file-name-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(modify-coding-system-alist 'process "*" 'utf-8)
;; Ensure environment variables inside Emacs look the same as in the user's shell
(use-package exec-path-from-shell
:init
(setq exec-path-from-shell-variables '("PATH" "MANPATH")
exec-path-from-shell-arguments '("-l"))
:config
(exec-path-from-shell-initialize))
;; Start server
(use-package server
:straight nil
:hook (after-init . server-mode))
;; Go to the last place when previously visited the file
(use-package saveplace
:straight nil
:hook (after-init . save-place-mode))
(use-package recentf
:straight nil
:hook (after-init . recentf-mode)
:init
(setq recentf-max-saved-items 500
recentf-max-menu-items 15
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:" "/persp-confs/"
(lambda (file) (file-in-directory-p file package-user-dir))))
:config
(push (expand-file-name recentf-save-file) recentf-exclude)
(add-to-list 'recentf-filename-handlers #'abbreviate-file-name))
(use-package savehist
:straight nil
:hook (after-init . savehist-mode)
:init
(setq enable-recursive-minibuffers t ; Allow commands in minibuffers
history-length 1000
savehist-additional-variables '(mark-ring
global-mark-ring
search-ring
regexp-search-ring
extended-command-history)
savehist-autosave-interval 300))
(use-package simple
:straight nil
:hook ((after-init . size-indication-mode)
(text-mode . visual-line-mode)
(helpful-mode . visual-line-mode)
((prog-mode org-mode markdown-mode conf-mode latex-mode) . (lambda () (setq show-trailing-whitespace t))))
:init
(setq column-number-mode t
line-number-mode t
;; kill-whole-line t ; Kill line including '\n'
line-move-visual t
;; track-eol t ; Keep cursor at end of lines. Require line-move-visual is nil.
set-mark-command-repeat-pop t) ; Repeating C-SPC after popping mark pops it again
)
(use-package so-long
:straight nil
:hook (after-init . global-so-long-mode)
:config (setq so-long-threshold 400))
(use-package adaptive-wrap
:commands adaptive-wrap-prefix-mode)
(use-package keyfreq
:init
(setq keyfreq-file "~/.emacs.d/.keyfreq")
(setq keyfreq-file-lock "~/.emacs.d/.keyfreq.lock")
(keyfreq-mode 1)
(keyfreq-autosave-mode 1)
:config
(setq keyfreq-excluded-commands
'(self-insert-command
org-self-insert-command
forward-char
backward-char
previous-line
next-line))
(setq keyfreq-excluded-regexp
'("\\`vertico-.*\\'"
"\\`iscroll-.*\\'"
"\\`vterm-.*\\'")))
;; Misc
(fset 'yes-or-no-p 'y-or-n-p)
(setq-default major-mode 'emacs-lisp-mode
tab-width 8
indent-tabs-mode nil) ; Permanently indent with spaces, never with TABs
;; flash the modeline for visual bell
(setq visible-bell nil
ring-bell-function 'flash-mode-line)
(setq inhibit-compacting-font-caches t ; Don’t compact font caches during GC.
delete-by-moving-to-trash t ; Deleting files go to OS's trash folder
make-backup-files nil ; Forbide to make backup files
create-lockfiles nil ; Forbide to make lockfiles
auto-save-default nil ; Disable auto save
uniquify-buffer-name-style 'post-forward-angle-brackets ; Show path if names are same
adaptive-fill-regexp "[ t]+|[ t]*([0-9]+.|*+)[ t]*"
adaptive-fill-first-line-regexp "^* *$"
sentence-end-double-space nil)
;; Use the system clipboard
(setq select-enable-clipboard t)
;; Always focus the help window
(setq help-window-select t)
;; Enable mouse in terminal mode
(xterm-mouse-mode)
;; Auto tangle this file after save (without prompt)
(add-hook 'after-save-hook #'org-babel-auto-tangle)
;; 初始 *scratch* buffer 使用 fundamental-mode
(setq initial-major-mode 'fundamental-mode)
;; Disable scratch buffer text
(setq initial-scratch-message nil)
(provide 'init-basic)
Only prepare the packages here, specific keybindings goes to init-map.el
.
;;; init-general.el --- Initialize general -*- lexical-binding: t -*-
;;; Code:
(use-package key-chord
:diminish
:init
(key-chord-mode))
(use-package general)
(provide 'init-general)
;;; init-ui.el --- Better lookings and appearances. -*- lexical-binding: t -*-
;;; Code:
;; Title
(setq frame-title-format '((:eval (if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b"))
" "
user-login-name
"@"
system-name)
icon-title-format frame-title-format)
;; Optimization
(setq idle-update-delay 1.0)
(setq-default cursor-in-non-selected-windows nil)
(setq highlight-nonselected-windows nil)
(tooltip-mode -1) ;; Disable tooltips
(set-fringe-mode 10) ;; 左右边框 仅对GUI生效
(global-hl-line-mode t)
;; 分屏偏好可自定义
(defgroup solarion-window nil
"Solarion 的窗口分屏偏好。"
:group 'windows)
(defcustom solarion-split-height-threshold nil
"赋值给 `split-height-threshold` 的值。nil 表示不进行水平分割(上下分屏)。"
:type '(choice (const :tag "禁止水平分割" nil)
integer)
:group 'solarion-window)
(defcustom solarion-split-width-threshold 80
"赋值给 `split-width-threshold` 的值。80 表示在宽度大于等于 80 时进行垂直分割(左右分屏)。"
:type '(choice (const :tag "禁止垂直分割" nil)
integer)
:group 'solarion-window)
(setq split-height-threshold solarion-split-height-threshold
split-width-threshold solarion-split-width-threshold)
;; Mode-line
(defun solarion-apply-modeline-fonts ()
"应用 modeline 字体(避免主题覆盖)。"
(when (display-graphic-p)
(set-face-attribute 'mode-line nil :font solarion-font-default :height solarion-font-size)
(set-face-attribute 'mode-line-inactive nil :font solarion-font-default :height solarion-font-size)))
;; 确保切换主题后也保持自定义的 modeline 字体
(advice-add 'enable-theme :after (lambda (&rest _) (solarion-apply-modeline-fonts)))
(use-package doom-modeline
:diminish doom-modeline-mode
:init
(setq doom-modeline-modal-icon nil)
;; Must use mono font here
(solarion-apply-modeline-fonts)
(doom-modeline-mode t))
(use-package nerd-icons
:init
(setq nerd-icons-font-family solarion-font-default))
(use-package display-line-numbers
:straight nil
:init
(setq display-line-numbers-width-start t)
(setq display-line-numbers-current-absolute t)
:config
(dolist (mode '(c-mode-common-hook
c-mode-hook
emacs-lisp-mode-hook
lisp-interaction-mode-hook
lisp-mode-hook
sh-mode-hook
python-mode-hook
html-mode-hook
rust-mode-hook
conf-mode-hook))
(add-hook mode (lambda () (setq display-line-numbers t)))))
;; Suppress GUI features
(setq use-file-dialog nil
use-dialog-box nil
inhibit-startup-screen t
inhibit-startup-echo-area-message t)
;; Display dividers between windows
(setq window-divider-default-places t
window-divider-default-bottom-width 1
window-divider-default-right-width 1)
(add-hook 'window-setup-hook #'window-divider-mode)
(add-to-list 'default-frame-alist '(fullscreen . maximized))
(use-package rainbow-delimiters
:hook
(prog-mode . rainbow-delimiters-mode)
(LaTeX-mode . rainbow-delimiters-mode)
:commands rainbow-delimiters-mode)
(use-package which-key
:diminish which-key-mode
:hook (after-init . which-key-mode)
:init
(setq which-key-idle-delay 0.2)
(setq which-key-sort-order 'which-key-key-order-alpha)
(setq which-key-prefix-prefix "")
:config
(set-face-attribute 'which-key-group-description-face nil :weight 'bold))
(use-package which-key-posframe
:after which-key
:init
(which-key-posframe-mode)
:config
(setq which-key-posframe-parameters
'((left-fringe . 8)
(right-fringe . 8))))
(use-package command-log-mode
:commands command-log-mode)
(use-package helpful
:bind
([remap describe-function] . helpful-callable)
([remap describe-variable] . helpful-variable)
([remap describe-key] . helpful-key)
:commands (helpful-callable helpful-variable helpful-key))
(use-package winum
:hook (after-init . winum-mode))
(use-package posframe)
(use-package shackle
:hook (after-init . shackle-mode)
:init
(setq shackle-default-alignment 'right) ; default below
(setq shackle-select-reused-windows t)
(setq shackle-rules
'(("*vterm*" :size 0.3 :align below :popup t)
;; (compilation-mode :ignore t)
;; ("\\*Async Shell.*\\*" :regexp t :ignore t)
;; ("\\*corfu.*\\*" :regexp t :ignore t)
;; ("*eshell*" :select t :size 0.4 :align t :popup t)
(helpful-mode :size 0.4 :align t :popup t)
(help-mode :size 0.4 :align t :popup t)
;; ("*Messages*" :select t :size 0.4 :align t :popup t)
(magit-status-mode :inhibit-window-quit t :other t)
(magit-log-mode :inhibit-window-quit t :other t)
("\\*Org Src.*\\*" :regexp t :inhibit-window-quit t :other t))))
(use-package ef-themes
:straight '(ef-themes :type git :host github :repo "protesilaos/ef-themes")
:init
(setq ef-themes-headings nil)
(setq ef-themes-mixed-fonts nil)
(setq ef-themes-variable-pitch-ui nil)
(ef-themes-select 'ef-cyprus))
(use-package valign
:hook (org-mode . valign-mode))
(provide 'init-ui)
Use iscroll for image scrolling and pixel-scroll-precision-mode for smooth scrolling (available since emacs 29)
;;; init-scroll.el --- Better scrolling effects. -*- lexical-binding: t -*-
;;; Code:
(setq scroll-preserve-screen-position 'always
next-screen-context-lines 5)
(use-package iscroll
:init
:hook (org-mode . iscroll-mode))
(when (fboundp 'pixel-scroll-precision-mode)
(pixel-scroll-precision-mode))
(provide 'init-scroll)
The font settings are mainly for GUI Emacs, this would not affect TUI Emacs. font check Chinese: 遍角次亮采之门 Symbols: ♪ Kana: 夜に駆ける
;;; init-fonts.el --- Fonts configurations (for GUI) -*- lexical-bindings: t -*-
;;; Code:
(defun solarion-config-fonts ()
"Apply font configuration based on user custom variables."
(when (display-graphic-p)
;; Default font
(set-face-attribute 'default nil :font solarion-font-default :height solarion-font-size)
;; Fixed-pitch (等宽字体)
(set-face-attribute 'fixed-pitch nil :font solarion-font-default :height solarion-font-size)
;; Variable-pitch(比例字体)
(set-face-attribute 'variable-pitch nil :font solarion-font-variable :height solarion-font-size)
;; CJK 字体
(set-fontset-font t 'han (font-spec :family solarion-font-cjk :weight 'semi-bold :slant 'normal))
(set-fontset-font t 'cjk-misc (font-spec :family solarion-font-cjk :weight 'semi-bold :slant 'normal))
(set-fontset-font t 'kana (font-spec :family "Noto Serif CJK JP" :weight 'semi-bold :slant 'normal))
;; Emoji
(set-fontset-font t 'symbol (font-spec :family solarion-font-emoji) nil 'prepend)))
(solarion-config-fonts)
(add-hook 'window-setup-hook #'solarion-config-fonts)
(add-hook 'server-after-make-frame-hook #'solarion-config-fonts)
;; https://github.com/mickeynp/ligature.el
(use-package ligature
:straight '(ligature :type git :host github :repo "mickeynp/ligature.el")
:config
;; Enable the "www" ligature in every possible major mode
(ligature-set-ligatures 't '("www"))
;; Enable traditional ligature support in eww-mode, if the
;; `variable-pitch' face supports it
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
;; Enable all Cascadia Code ligatures in programming modes
(ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\\\" "://"))
;; Enables ligature checks globally in all buffers. You can also do it
;; per mode with `ligature-mode'.
(global-ligature-mode t))
(provide 'init-fonts)
;;; init-edit.el --- Initialize editing configurations -*- lexical-binding: t -*-
;;; Code:
;; Automatically reload files was modified by external program
(use-package autorevert
:straight nil
:diminish
:init
(setq global-auto-revert-non-file-buffers t
auto-revert-interval 1
auto-revert-remote-files t)
(global-auto-revert-mode))
(use-package auto-save
:straight '(auto-save :type git :host github :repo "manateelazycat/auto-save")
:config
(auto-save-enable)
(setq auto-save-enable-predicate
(lambda () (not (string-match-p "\\*.*\\*" (buffer-name)))))
(setq auto-save-silent t) ; quietly save
(setq auto-save-delete-trailing-whitespace t) ; automatically delete spaces at the end of the line when saving
)
(use-package format-all
:commands (format-all-buffer format-all--language-id-buffer))
;; Jump to things in Emacs tree-style
(use-package avy
:config (setq avy-all-windows t
avy-background t
avy-style 'at-full
avy-timeout-seconds 0.5))
(use-package beginend
:diminish beginend-global-mode
:hook (after-init . beginend-global-mode))
;; A comprehensive visual interface to diff & patch
(use-package ediff
:straight nil
:hook (;; show org ediffs unfolded
(ediff-prepare-buffer . outline-show-all)
;; restore window layout when done
;; (ediff-quit . winner-undo)
)
:config
(setq ediff-window-setup-function 'ediff-setup-windows-plain
ediff-split-window-function 'split-window-vertically
ediff-merge-split-window-function 'split-window-vertically))
;; Increase selected region by semantic units
(use-package expand-region
:commands er/expand-region)
;; Hungry deletion
(use-package hungry-delete
:diminish
:hook (after-init . global-hungry-delete-mode)
:init (setq hungry-delete-except-modes '(help-mode minibuffer-mode minibuffer-inactive-mode calc-mode)
hungry-delete-chars-to-skip "\f�"))
;; Move to the beginning/end of line or code
(use-package mwim
:config
(general-define-key
:keymaps 'override
"C-a" #'mwim-beginning-of-code-or-line
"C-e" #'mwim-end-of-code-or-line))
(general-def "C-/" #'undo-only)
(general-def "C-r" #'undo-redo)
;; vundo
(use-package vundo
:config
(general-def "C-x u" #'vundo)
(general-def vundo-mode-map "C-n" #'vundo-next)
(general-def vundo-mode-map "C-p" #'vundo-previous)
(general-def vundo-mode-map "C-f" #'vundo-forward)
(general-def vundo-mode-map "C-b" #'vundo-backward)
(setq vundo-glyph-alist vundo-unicode-symbols))
;; Handling capitalized subwords in a nomenclature
(use-package subword
:straight nil
:diminish
:hook ((prog-mode . subword-mode)
(minibuffer-setup . subword-mode)))
(use-package sudo-edit
:commands (sudo-edit-find-file sudo-edit-current-file))
;; On-the-fly spell checker
(use-package flyspell
:straight nil
:diminish
:if (executable-find "aspell")
:hook
(((text-mode outline-mode) . flyspell-mode)
(prog-mode . flyspell-prog-mode)
(LaTeX-mode . flyspell-mode)
(flyspell-mode . (lambda ()
(dolist (key '("C-;" "C-," "C-."))
(unbind-key key flyspell-mode-map)))))
:init
(setq flyspell-issue-message-flag nil
ispell-program-name "aspell"
ispell-extra-args '("--sug-mode=ultra" "--lang=en_US" "--run-together")))
;; Framework for mode-specific buffer indexes
(use-package imenu
:straight nil
:init
(setq imenu-auto-rescan t))
;; 中英文间自动加入空格
(use-package pangu-spacing
:diminish pangu-spacing-mode
:hook ((text-mode . pangu-spacing-mode)
(org-mode . pangu-spacing-mode))
:init
(setq pangu-spacing-real-insert-separator t))
;; occur
(add-hook 'occur-hook (lambda () (switch-to-buffer-other-window "*Occur*")))
;; smartparens
(use-package smartparens
:diminish
:config
(require 'smartparens-config)
(add-hook 'org-mode-hook #'smartparens-mode)
(add-hook 'LaTeX-mode-hook #'smartparens-mode)
(add-hook 'emacs-lisp-mode-hook #'smartparens-mode)
;; custom pairs
(with-eval-after-load 'org
(sp-local-pair 'org-mode "\\[" "\\]")
(sp-local-pair 'org-mode "=" nil :actions :rem)
(sp-local-pair 'org-mode "/" nil :actions :rem)))
(use-package lorem-ipsum)
;; conf-mode
(defcustom solarion/conf-indent-offset 2
"Indent width for brace-based conf indentation."
:type 'integer :group 'editing)
(defun solarion/conf--brace-level (pos)
"Count brace nesting before POS, skipping comments/strings."
(save-excursion
(goto-char (point-min))
(let ((lvl 0))
(while (re-search-forward "[{}]" pos t)
(unless (nth 8 (syntax-ppss)) ; 在注释或字符串中则跳过
(if (equal (char-before) ?{)
(cl-incf lvl)
(cl-decf lvl))))
(max 0 lvl))))
(defun solarion/conf-indent-line ()
"Brace-based indent: closing '}' outdents by one level."
(interactive)
(let* ((bol (save-excursion (back-to-indentation) (point)))
(closing (save-excursion (goto-char bol) (looking-at-p "}")))
(lvl (solarion/conf--brace-level bol))
(target (* solarion/conf-indent-offset (max 0 (if closing (1- lvl) lvl)))))
(indent-line-to target)))
(define-minor-mode solarion/brace-indent-mode
"Generic brace-based indentation for conf-like files."
:lighter " { }"
(if solarion/brace-indent-mode
(progn
(setq-local indent-line-function #'solarion/conf-indent-line)
(setq-local indent-tabs-mode nil))
(kill-local-variable 'indent-line-function)))
(defun solarion/conf--line-str ()
(string-trim (buffer-substring-no-properties
(line-beginning-position) (line-end-position))))
(defun solarion/conf--skip-comment-or-blank-p (s)
(or (string-empty-p s) (string-prefix-p "#" s)))
(defun solarion/conf--yaml-p ()
"Heuristic: YAML keys with ':', list items '-', or '---' header."
(save-excursion
(goto-char (point-min))
(let ((checked 0) (hits 0))
(while (and (< checked 60) (not (eobp)))
(let ((s (solarion/conf--line-str)))
(unless (solarion/conf--skip-comment-or-blank-p s)
(cl-incf checked)
(when (or
(string-match-p "^[[:space:]]*---[[:space:]]*$" s)
(string-match-p "^[[:space:]]*-[[:space:]]" s)
(string-match-p "^[[:space:]]*\\(?:[A-Za-z0-9_.-]+\\|\"[^\"]+\"\\|'[^']+'\\):[[:space:]]" s))
(cl-incf hits))))
(forward-line 1))
(>= hits 2))))
(defun solarion/conf--toml-p ()
"Heuristic: [section] or repeated key = value lines."
(save-excursion
(goto-char (point-min))
(let ((checked 0) (assigns 0) (has-section nil))
(while (and (< checked 80) (not (eobp)))
(let ((s (solarion/conf--line-str)))
(unless (solarion/conf--skip-comment-or-blank-p s)
(cl-incf checked)
(when (or (string-match-p "^\\[[^]]+\\]$" s)
(string-match-p "^\\[\\[[^]]+\\]\\]$" s))
(setq has-section t))
(when (string-match-p "^[A-Za-z0-9_.-]+\\s*=\\s*.+$" s)
(cl-incf assigns))))
(forward-line 1))
(or has-section (>= assigns 3)))))
(defun solarion/conf--looks-like-brace-conf-p ()
(save-excursion
(goto-char (point-min))
(re-search-forward "^[[:space:]]*[^#\n]*{[[:space:]]*$" nil t)))
(defun solarion/conf--maybe-switch-mode ()
"Prefer YAML/TOML. Else enable brace indent."
(cond
((solarion/conf--yaml-p) (yaml-mode))
((solarion/conf--toml-p) (toml-mode))
((solarion/conf--looks-like-brace-conf-p)
(solarion/brace-indent-mode 1))))
(add-hook 'conf-unix-mode-hook
(lambda ()
(solarion/conf--maybe-switch-mode)))
(use-package prettier-js
:hook
('css-mode . 'prettier-js-mode)
('json-mode . 'prettier-js-mode)
('markdown-mode . 'prettier-js-mode)
('html-mode . 'prettier-js-mode)
('js-mode . 'prettier-js-mode)
('web-mode . 'prettier-js-mode)
)
(provide 'init-edit)
;;; init-treesit.el --- Treesit settings -*- lexical-binding: t -*-
;;; Code:
(setq treesit-language-source-alist
'((bash "https://github.com/tree-sitter/tree-sitter-bash" nil "src")
(c "https://github.com/tree-sitter/tree-sitter-c" nil "src")
(cpp "https://github.com/tree-sitter/tree-sitter-cpp" nil "src")
(css "https://github.com/tree-sitter/tree-sitter-css" nil "src")
(go "https://github.com/tree-sitter/tree-sitter-go" nil "src")
(html "https://github.com/tree-sitter/tree-sitter-html" nil "src")
(java "https://github.com/tree-sitter/tree-sitter-java" nil "src")
(javascript "https://github.com/tree-sitter/tree-sitter-javascript" "master" "src")
(typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
(json "https://github.com/tree-sitter/tree-sitter-json" nil "src")
(python "https://github.com/tree-sitter/tree-sitter-python" nil "src")
(rust "https://github.com/tree-sitter/tree-sitter-rust" nil "src")
(toml "https://github.com/tree-sitter/tree-sitter-toml" nil "src")
(yaml "https://github.com/ikatyang/tree-sitter-yaml" nil "src")
))
(defun solarion/treesit-ensure (&rest langs)
"安装缺失的语法解析器。"
(dolist (lang langs)
(unless (treesit-language-available-p lang)
(treesit-install-language-grammar lang))))
;; 按需安装(第一次配置后执行一次即可)
;; (solarion/treesit-ensure 'bash 'c 'cpp 'css 'go 'html 'java
;; 'javascript 'typescript 'tsx 'json
;; 'python 'rust 'toml 'yaml)
;; 将传统主模式自动映射到 *-ts-mode(可按需删减)
(setq major-mode-remap-alist
'((c-mode . c-ts-mode)
(c++-mode . c++-ts-mode)
(c-or-c++-mode . c-or-c++-ts-mode)
(conf-toml-mode . toml-ts-mode)
(css-mode . css-ts-mode)
(js-mode . js-ts-mode)
(javascript-mode . js-ts-mode)
(js-json-mode . json-ts-mode)
(json-mode . json-ts-mode)
(python-mode . python-ts-mode)
(sh-mode . bash-ts-mode)
(typescript-mode . typescript-ts-mode)
(yaml-mode . yaml-ts-mode)
))
(use-package nix-ts-mode
:mode "\\.nix\\'")
;; 源码内容相对 #+begin_src 向右缩 2 列
(setq org-src-preserve-indentation nil) ;; 关闭"保留原缩进"
(setq org-edit-src-content-indentation 2) ;; 内容右移 2 列
(setq org-src-tab-acts-natively t) ;; 在子缓冲里用主模式缩进
;; 缓冲缩进(优先走当前主模式的缩进器;若是 *-ts-mode 就是 treesit)
(defun indent-buffer (&optional beg end)
"缩进当前缓冲或给定区域。默认整个缓冲区。"
(interactive)
(save-excursion
(save-restriction
(widen)
(let* ((rb (or beg (point-min)))
(re (or end (point-max)))
;; Makefile 等保持 TAB
(indent-tabs-mode
(and indent-tabs-mode (derived-mode-p 'makefile-mode))))
(indent-region rb re nil)))))
(defun solarion/indent-buffer-and-format nil
"先 treesit/主模式缩进,再调用外部 formatter,最后清理行尾空白。"
(interactive)
(indent-buffer)
(when (derived-mode-p 'prog-mode 'conf-mode 'text-mode)
(ignore-errors (delete-trailing-whitespace))))
(provide 'init-treesit)
;;; init-hydra.el --- Hydra configurations -*- lexical-binding: t -*-
;;; Code:
(use-package hydra
:config
(defhydra solarion/hydra-window-resize (:timeout 4)
"Resize window"
("j" enlarge-window "Increase height")
("k" shrink-window "Decrease height")
("h" shrink-window-horizontally "Decrease width")
("l" enlarge-window-horizontally "Increase width")
("SPC" balance-windows "Balance windows")
("q" nil "quit" :exit t)))
(provide 'init-hydra)
Define the majority of keybindings here.
;;; init-map.el --- Keybindings -*- lexical-binding: t -*-
;;; Code:
;; misc
(general-def [f10] #'solarion/indent-buffer-and-format) ;; f12 reserved for yakuake
(general-def [f5] #'revert-buffer)
(general-def ";" (general-key-dispatch 'self-insert-command
:timeout 0.25
"'" #'comment-line))
(general-def "j" (general-key-dispatch 'self-insert-command
:timeout 0.25
"k" (general-key "C-g")))
(general-def "k" (general-key-dispatch 'self-insert-command
:timeout 0.25
"j" #'avy-goto-char-timer))
(general-def :keymaps 'override "C-c k" #'copy-line)
(general-unbind "M-`") ;; reserved for tmux
(general-create-definer global-leader-def
:keymaps 'override
:prefix "C-c")
(general-create-definer local-leader-def
:keymaps 'override
:prefix "C-c m")
;; Global leader
(global-leader-def
;; maps
"h" #'(help-command :which-key "Help")
;; keys
"C-." #'consult-imenu ;; "C-c ." for org-time-stamp
"=" #'er/expand-region
"C-s" #'consult-ripgrep
"C-SPC" #'consult-mark
;; window
"w" '(:ignore t :which-key "Window")
"ws" #'split-window-below-and-focus
"wv" #'split-window-right-and-focus
"wd" #'(delete-window :which-key "Delete window")
"wq" #'(kill-buffer-and-window :which-key "Kill buffer and window")
"wr" #'(solarion/hydra-window-resize/body :which-key "Window Resize")
"w=" #'(balance-windows :which-key "Balance Windows")
"1" #'(winum-select-window-1 :which-key "Switch to window 1")
"2" #'(winum-select-window-2 :which-key "Switch to window 2")
"3" #'(winum-select-window-3 :which-key "Switch to window 3")
"4" #'(winum-select-window-4 :which-key "Switch to window 4")
"5" #'(winum-select-window-5 :which-key "Switch to window 5")
;; buffer & bookmark
"b" '(:ignore t :which-key "Buffer/Bookmark")
"bp" #'(previous-buffer :which-key "Previous Buffer")
"bn" #'(next-buffer :which-key "Next Buffer")
"bb" #'(consult-buffer :which-key "Switch Buffer")
"bc" #'(clone-indirect-buffer :which-key "Clone Buffer")
"bd" #'(kill-current-buffer :which-key "Kill Buffer")
"bi" #'ibuffer
"bm" #'(bookmark-set :which-key "Set Bookmark")
"bM" #'(bookmark-delete :which-key "Delete Bookmark")
"bj" #'(consult-bookmark :which-key "Jump to Bookmark")
"bl" #'(list-bookmarks :which-key "Bookmarks List")
"bN" #'(solarion-new-buffer :which-key "New Empty Buffer")
"br" #'(revert-buffer :which-key "Revert Buffer")
;; file
"f" '(:ignore t :which-key "File")
"fd" #'(dired-jump :which-key "Dired Jump")
"fD" #'(delete-file-and-buffer :which-key "Delete File")
"ff" #'(find-file :which-key "Find File")
"fs" #'(save-buffer :which-key "Save File")
"fS" #'(write-file :which-key "Save File As")
"fr" #'(consult-recent-file :which-key "Recent Files")
"fp" #'(solarion-edit-config :which-key "Edit Config")
;; quit
"q" '(:ignore t :which-key "Quit")
"qf" #'(delete-frame :which-key "Delete Frame")
"qq" #'(save-buffers-kill-terminal :which-key "Quit Emacs")
;; git
"g" '(:ignore t :which-key "Git")
"gR" #'vc-revert
"g/" #'magit-dispatch
"g." #'magit-file-dispatch
;; "g'" #'forge-dispatch
"gb" #'magit-branch-checkout
"gg" #'magit-status
"gG" #'solarion/git-add-commit-push
"gD" #'magit-file-delete
"gB" #'magit-blame
"gC" #'magit-clone
"gF" #'magit-fetch
"gL" #'magit-log-buffer-file
"gS" #'magit-stage-file
"gU" #'magit-unstage-file
"gf" '(:ignore t :which-key "find")
"gff" #'magit-find-file
"gfg" #'magit-find-git-config-file
"gfc" #'magit-show-commit
;; "gfi" #'forge-visit-issue
;; "gfp" #'forge-visit-pullreq
"gl" '(:ignore t :which-key "list")
"glr" #'magit-list-repositories
"gls" #'magit-list-submodules
;; "gli" #'forge-list-issues
;; "glp" #'forge-list-pullreqs
;; "gln" #'forge-list-notifications
"gc" '(:ignore t :which-key "create")
"gcr" #'magit-init
"gcR" #'magit-clone
"gcc" #'magit-commit-create
"gcf" #'magit-commit-fixup
"gcb" #'magit-branch-and-checkout
;; "gci" #'forge-create-issue
;; "gcp" #'forge-create-pullreq
;; custom
"o" '(:ignore t :which-key "Custom Entry")
"oT" #'(consult-theme :which-key "Choose Theme")
"ot" #'(ef-themes-load-random :which-key "Random Theme")
"oo" #'occur
"oy" #'yadm
)
;; Local leader
;; org-mode
(local-leader-def org-mode-map
"," #'org-switchb
"." #'consult-org-heading
"b" #'org-mark-ring-goto
;; 插入结构化模板(如 src/example 等)
"s" #'org-insert-structure-template
"a" '(:ignore t :which-key "Attach")
"aa" #'org-attach
"ap" #'solarion/org-download-paste-clipboard-wsl
"e" #'(org-export-dispatch :which-key "Export")
"d" '(:ignore t :which-key "Date")
"dd" #'org-deadline
"ds" #'org-schedule
"dt" #'org-time-stamp
"dT" #'org-time-stamp-inactive
"f" #'org-footnote-action
"h" #'org-toggle-heading
"i" #'org-toggle-item
"p" '(:ignore t :which-key "Priority")
"pd" #'org-priority-down
"pp" #'org-priority
"pu" #'org-priority-up
"R" #'org-refile
"t" #'org-todo
"x" #'org-toggle-checkbox)
(general-def help-map
;; new keybinds
"'" #'describe-char
;; Unbind `help-for-help'. Conflicts with which-key's help command for the
;; <leader> h prefix. It's already on ? and F1 anyway.
"C-h" nil
;; replacement keybinds
;; replaces `info-emacs-manual' b/c it's on C-m now
"r" nil
"b" #'describe-bindings
"B" #'general-describe-keybindings
;; replaces `apropos-command'
"a" #'apropos
"A" #'apropos-documentation
;; replaces `describe-copying' b/c not useful
"C-c" #'describe-coding-system
;; replaces `Info-got-emacs-command-node' b/c redundant w/ `Info-goto-node'
"F" #'describe-face
;; replaces `view-hello-file' b/c annoying
"h" nil
;; replaces `help-with-tutorial', b/c it's less useful than `load-theme'
"t" #'consult-theme
;; replaces `finder-by-keyword' b/c not useful
"p" nil)
(provide 'init-map)
The bundle of vertico, consult, orderless, marginalia and embark
;;; init-vertico.el --- Initialize the vertico bundle -*- lexical-binding: t -*-
;;; Code:
(use-package vertico
:straight (vertico :files (:defaults "extensions/*"))
:init
(vertico-mode)
(setq vertico-scroll-margin 2)
;; Show 10 candidates
(setq vertico-count 10)
;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
(setq vertico-cycle t))
(use-package vertico-posframe
:after vertico
:init
(vertico-posframe-mode 1)
:config
(setq vertico-posframe-parameters
'((left-fringe . 8)
(right-fringe . 8))))
(use-package vertico-directory
:after vertico
:straight nil
;; More convenient directory navigation commands
:bind (:map vertico-map
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("C-DEL" . vertico-directory-delete-word))
;; Tidy shadowed file names
:hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
(use-package nerd-icons-completion
:after marginalia
:hook (marginalia-mode . nerd-icons-completion-marginalia-setup)
:config
(nerd-icons-completion-mode 1))
(use-package pinyinlib
:after orderless
:config
(defun completion--regex-pinyin (str)
(orderless-regexp (pinyinlib-build-regexp-string str)))
(add-to-list 'orderless-matching-styles 'completion--regex-pinyin))
(use-package orderless
:init
;; Configure a custom style dispatcher (see the Consult wiki)
;; (setq orderless-style-dispatchers '(+orderless-dispatch)
;; orderless-component-separator #'orderless-escapable-split-on-space)
(setq completion-styles '(basic orderless)
completion-category-defaults nil
completion-category-overrides '((file (styles basic partial-completion)))))
;; Enable richer annotations using the Marginalia package
(use-package marginalia
;; Either bind `marginalia-cycle` globally or only in the minibuffer
;; The :init configuration is always executed (Not lazy!)
:init
;; Must be in the :init section of use-package such that the mode gets
;; enabled right away. Note that this forces loading the package.
(marginalia-mode))
(use-package consult
:bind
("C-s" . consult-line)
([remap switch-to-buffer] . consult-buffer)
([remap yank-pop] . consult-yank-pop)
:config
(setq consult-fontify-preserve nil))
(use-package embark
:bind
("C-." . embark-act)
("M-." . embark-dwim)
;; Optionally replace the key help with a completing-read interface
:init
(setq prefix-help-command #'embark-prefix-help-command))
(use-package embark-consult
:after (embark consult)
:demand t
;; only necessary if you have the hook below
;; if you want to have consult previews as you move around an
;; auto-updating embark collect buffer
:hook
(embark-collect-mode . consult-preview-at-point-mode))
(use-package wgrep)
(provide 'init-vertico)
Corfu enhances completion at point with a small completion popup. The current candidates are shown in a popup below or above the point. Corfu is the minimalistic completion-in-region counterpart of the Vertico minibuffer UI.
;;; init-corfu.el --- Completion Overlay Region FUnction -*- lexical-binding: t -*-
;;; Code:
(use-package corfu
:init
;; corfu settings
(setq corfu-cycle t)
(setq corfu-auto t)
(setq corfu-auto-prefix 2)
(setq corfu-auto-delay 0)
(setq corfu-count 7)
(setq corfu-preselect-first nil) ;; tab for complete common
;; emacs settings
;; TAB cycle if there are only few candidates
(setq completion-cycle-threshold 5)
;; Enable indentation+completion using the TAB key.
;; `completion-at-point' is often bound to M-TAB.
(setq tab-always-indent 'complete)
;; Emacs 30 及更新:可替换 ispell 的补全为 cape-dict(向后兼容 Emacs 29)
(when (boundp 'text-mode-ispell-word-completion)
(setq text-mode-ispell-word-completion 'cape-dict))
;; Hide commands in M-x which do not apply to the current mode. Corfu
;; commands are hidden, since they are not used via M-x. This setting is
;; useful beyond Corfu.
(setq read-extended-command-predicate #'command-completion-default-include-p)
:config
(global-corfu-mode)
)
(use-package cape
:init
;; Add `completion-at-point-functions', used by `completion-at-point'.
(setq cape-dabbrev-check-other-buffers nil) ;; only check current buffer for completion
;; 将 dabbrev 放在 CAPF 链最后,避免影响语言专用补全
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-dabbrev t))
(use-package nerd-icons-corfu
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter)
(setq nerd-icons-corfu-mapping
'((array :style "cod" :icon "symbol_array" :face font-lock-type-face)
(boolean :style "cod" :icon "symbol_boolean" :face font-lock-builtin-face)
;; You can alternatively specify a function to perform the mapping,
;; use this when knowing the exact completion candidate is important.
(file :fn nerd-icons-icon-for-file :face font-lock-string-face)
;; ...
(t :style "cod" :icon "code" :face font-lock-warning-face)))
;; Remember to add an entry for `t', the library uses that as default.
)
(provide 'init-corfu)
Tempel is a tiny template package for Emacs, which uses the syntax of the Emacs Tempo library. A substitute for yasnippet.
;;; init-tempel.el --- Simple templates for Emacs -*- lexical-binding: t -*-
;;; Code:
;; Configure Tempel
(use-package tempel
:bind
(:map tempel-map
([tab] . tempel-next))
:init
(setq tempel-path
(list (expand-file-name "tempel-templates/*.eld" user-emacs-directory)))
(setq tempel-trigger-prefix "<")
;; Setup completion at point
(defun tempel-setup-capf ()
;; Add the Tempel Capf to `completion-at-point-functions'.
;; `tempel-expand' only triggers on exact matches. Alternatively use
;; `tempel-complete' if you want to see all matches, but then you
;; should also configure `tempel-trigger-prefix', such that Tempel
;; does not trigger too often when you don't expect it. NOTE: We add
;; `tempel-expand' *before* the main programming mode Capf, such
;; that it will be tried first.
(setq-local completion-at-point-functions
(cons #'tempel-complete
completion-at-point-functions)))
(add-hook 'prog-mode-hook 'tempel-setup-capf)
(add-hook 'text-mode-hook 'tempel-setup-capf))
(provide 'init-tempel)
;;; init-magit.el --- Configuration related to git -*- lexical-binding: t -*-
;;; Code:
(use-package magit
:init
(setq magit-display-buffer-function #'magit-display-buffer-traditional)
:config
(define-key magit-mode-map (kbd "p") #'magit-push)
(define-key magit-mode-map (kbd "P") #'magit-push-current-to-pushremote)
;; 在 git-commit 缓冲中,将 C-c . 绑定到 org-time-stamp
(with-eval-after-load 'git-commit
(require 'org)
(define-key git-commit-mode-map (kbd "C-c .") #'org-time-stamp)))
(use-package diff-hl
:diminish
:init
(global-diff-hl-mode)
(add-hook 'magit-pre-refresh-hook 'diff-hl-magit-pre-refresh)
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))
(provide 'init-magit)
;;; init-ibuffer.el --- Initialize ibuffer configurations -*- lexical-binding: t -*-
;;; Code:
(defconst solarion/ibuffer-custom-groups
'(("Dired" (mode . dired-mode))
("Org" (mode . org-mode))
("Emacs" (or (name . "^\\*scratch\\*$")
(name . "^\\*Backtrace\\*$")
(name . "^\\*Messages\\*$")))
("Help" (or (name . "Help")
(name . "^helpful")))
("Magit" (name . "^magit")))
"Static ibuffer groups for frequently visited buffers.")
(defun solarion/ibuffer-project-groups ()
"Return ibuffer groups built from project roots when available."
(when (require 'ibuffer-project nil t)
(ibuffer-project-generate-filter-groups)))
(defun solarion/ibuffer-group-by-project-and-kind ()
"Enable project groups followed by custom fallback groups."
(ibuffer-auto-mode 1)
(setq ibuffer-filter-groups
(append (solarion/ibuffer-project-groups)
solarion/ibuffer-custom-groups))
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic)))
(use-package ibuffer
:straight nil
:hook (ibuffer-mode . solarion/ibuffer-group-by-project-and-kind)
:custom
(ibuffer-show-empty-filter-groups nil)
(ibuffer-saved-filter-groups
`(("custom" ,@solarion/ibuffer-custom-groups))))
(use-package ibuffer-project
:straight nil
:after ibuffer
:commands (ibuffer-project-generate-filter-groups))
(provide 'init-ibuffer)
Clean inactive buffers.
;;; init-midnight.el --- Configurations for midnight -*- lexical-binding: t -*-
;;; Code:
;; use `clean-buffer-list' from `midnight.el'
(use-package midnight
:config
;; kill buffers if they were last disabled more than this seconds ago
(setq clean-buffer-list-delay-special (* 3 60 60))
;; run clean-buffer-list every 30 minites
(setq clean-buffer-list-timer (run-at-time t 1800 'clean-buffer-list))
;; kill everything, clean-buffer-list is very intelligent at not killing
;; unsaved buffer.
;; 满足条件且超过`clean-buffer-list-delay-special'的buffer才会被清除
(setq clean-buffer-list-kill-regexps '("^.*$"))
(defvar solarion-clean-buffer-list-kill-never-buffer-names
(delq nil
(list "*httpd*" "*Messages*" "*Backtrace*" "*scratch*" "*Ibuffer*"
(when (boundp 'dashboard-buffer-name) dashboard-buffer-name)
(when (boundp 'vterm-buffer-name) vterm-buffer-name)
(when (boundp 'minimap-buffer-name) minimap-buffer-name)))
"Buffer names never to kill.")
(setq clean-buffer-list-kill-never-buffer-names
(append solarion-clean-buffer-list-kill-never-buffer-names clean-buffer-list-kill-never-buffer-names))
(defvar solarion-clean-buffer-list-kill-never-regexps
nil
"regexps not to kill")
(setq clean-buffer-list-kill-never-regexps
(append solarion-clean-buffer-list-kill-never-regexps clean-buffer-list-kill-never-regexps)))
(provide 'init-midnight)
;;; init-tramp.el --- Tramp settings -*- lexical-binding: t -*-
;;; Code:
(use-package tramp
:straight (:type built-in)
:config
(add-to-list 'tramp-methods
'("yadm"
(tramp-login-program "yadm")
(tramp-login-args (("enter")))
(tramp-login-env (("SHELL" . "/bin/sh")))
(tramp-remote-shell "/bin/sh")
(tramp-remote-shell-args ("-c"))))
(defun yadm ()
(interactive)
(magit-status "/yadm::")))
;; NOTE: Ensure `yadm` is installed and available in PATH for the
;; custom TRAMP method above.
(provide 'init-tramp)
;;; init-org.el --- Org-mode -*- lexical-binding: t -*-
;;; Code:
(use-package org
:straight (:type built-in)
:hook
(org-mode . solarion/org-mode-setup)
(org-mode . org-num-mode)
:config
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t)
(latex . t)
(python . t)
(matlab . t)
(gnuplot . t)))
(setq org-startup-with-inline-images nil)
(setq org-startup-with-latex-preview nil)
(setq org-adapt-indentation t);; add indentation for newlines
(setq org-highlight-latex-and-related '(native script entities))
(setq org-directory "~/org")
(setq org-ellipsis "[+]")
(setq org-tags-column -80)
(setq org-log-done 'time)
(setq org-hide-emphasis-markers nil) ;; Show bold and italic verbosely
(setq org-link-descriptive t) ;; Show links verbosely
(setq org-hide-leading-stars t)
(setq org-return-follows-link t)
;; export settings
(setq org-export-with-tags nil)
(setq org-export-with-sub-superscripts '{})
;; latex settings
;; .latexmkrc for reference
;; # --- Engine & output ---
;; $pdf_mode = 5; # 5 = XeLaTeX -> PDF
;; # $out_dir = './LaTeX.out';
;; $aux_dir = './LaTeX.aux';
;; $synctex = 1;
;; # Keep going through errors (use -halt-on-error instead if you prefer strict)
;; $xelatex = 'xelatex -file-line-error -interaction=nonstopmode %O %S';
;; # Use biber with biblatex
;; $bibtex = 'biber %O %B';
;; # --- Nomenclature (nlo -> nls) ---
;; add_cus_dep('nlo', 'nls', 0, 'makenomenclature');
;; sub makenomenclature {
;; system("makeindex -s nomencl.ist -o \"$_[0].nls\" \"$_[0].nlo\"");
;; }
;; # --- Cleaning: include nomencl, biber, and common aux files ---
;; push @generated_exts, qw(
;; nls nlo ilg ind idx
;; bcf run.xml bbl blg
;; synctex.gz toc out lot lof aux fls fdb_latexmk nav snm
;; );
(setq org-latex-pdf-process
'("latexmk -xelatex -shell-escape -f -interaction=nonstopmode %f && latexmk -c"))
;; 清理更多中间文件
(require 'ox-latex)
(setq org-latex-hyperref-template "\\hypersetup{\n pdfauthor={%a},\n pdftitle={%t},\n pdfkeywords={%k},\n pdfsubject={%d},\n pdfcreator={%c}, \n pdflang={%L}, hidelinks, colorlinks=false}\n")
(setq org-latex-logfiles-extensions
(append org-latex-logfiles-extensions
'("bcf" "run.xml" "bbl" "blg" "synctex.gz" "toc" "out" "lot" "lof"
"aux" "fls" "fdb_latexmk" "nav" "snm" "nls" "nlo" "ilg" "ind" "idx")))
(setq org-latex-toc-command "\\pagestyle{empty}\n\\tableofcontents\n\\clearpage\n\\setcounter{page}{1}\n\\pagestyle{plain}\n\n")
;; maketitle command
(setq org-latex-title-command "\\maketitle\n\\thispagestyle{empty}")
(setq org-latex-src-block-backend 'listings)
(setq org-export-with-smart-quotes t)
;; size of the preview latex fragments
(plist-put org-format-latex-options :scale 2)
(general-def org-src-mode-map "C-c C-c" #'org-edit-src-exit)
(general-def org-mode-map "C-RET" #'org-meta-return)
(general-def org-mode-map "C-<return>" #'org-meta-return)
;; org latex packages
(setq org-latex-packages-alist
'(("" "siunitx")
("" "amsmath")
("" "mathrsfs")
("scheme=plain" "ctex")
;; mlmodern is thicker
;; ("" "mlmodern")
("" "lmodern")
("T1" "fontenc")
("" "listings")))
(setq org-latex-listings-options
'(("breaklines" "true")
("breakatwhitespace" "false")
("basicstyle" "\\ttfamily\\small")
("columns" "flexible")
("keepspaces" "true")))
(setq org-image-actual-width t)
(setq org-preview-latex-image-directory (concat user-emacs-directory ".local/ltximg/"))
;; treesit
(defun solarion/org-set-lang-mode (lang mode-sym)
"把 org-src-lang-modes 里 LANG 的映射设为 MODE-SYM,先去重再插入到表头。"
(setq org-src-lang-modes
(cons (cons lang mode-sym)
(cl-remove-if (lambda (e) (string= (car e) lang))
org-src-lang-modes))))
;; —— 按需写入 treesit 映射(值是不带 -mode 的符号)————
(solarion/org-set-lang-mode "json" 'json-ts)
(solarion/org-set-lang-mode "toml" 'toml-ts)
(solarion/org-set-lang-mode "ini" 'toml-ts)
(solarion/org-set-lang-mode "javascript" 'js-ts)
(solarion/org-set-lang-mode "js" 'js-ts)
(solarion/org-set-lang-mode "typescript" 'typescript-ts)
(solarion/org-set-lang-mode "tsx" 'tsx)
(solarion/org-set-lang-mode "python" 'python-ts)
(solarion/org-set-lang-mode "yaml" 'yaml-ts)
(solarion/org-set-lang-mode "c" 'c-ts)
(solarion/org-set-lang-mode "cpp" 'c++-ts)
(solarion/org-set-lang-mode "bash" 'bash-ts)
(solarion/org-set-lang-mode "sh" 'bash-ts)
(solarion/org-set-lang-mode "nix" 'nix-ts)
)
(use-package ox-gfm
:config (add-to-list 'org-export-backends 'gfm))
(use-package org-superstar
:diminish org-superstar-mode
:hook (org-mode . (lambda () (org-superstar-mode)))
:init
(setq
org-superstar-headline-bullets-list '("■" "◆" "▲" "▶")
org-superstar-cycle-headline-bullets nil
org-superstar-prettify-item-bullets nil))
(use-package org-download
:after org
:config
(org-download-enable)
(setq org-download-method 'directory)
(defun solarion/org-download-image-dir ()
"Return a directory name based on current Org filename (e.g. asdf.org → asdf/)."
(when buffer-file-name
(let ((basename (file-name-base buffer-file-name)))
(expand-file-name (concat basename "/") (file-name-directory buffer-file-name)))))
(setq org-download-image-dir #'solarion/org-download-image-dir)
(setq
org-download-heading-lvl nil
org-download-timestamp "%Y%m%d-%H%M%S_")
;; to change image width seperately (also hide the annotate #+DOWNLOADED)
(setq org-download-annotate-function
(lambda (_link)
"#+ATTR_LATEX: :width \\linewidth\n#+ATTR_ORG: :width 400\n")))
;; Automatically insert table of contents after the heading with :TOC: tag
(use-package toc-org
:hook (org-mode . toc-org-mode))
;; Auto-toggle Org LaTeX fragments
(use-package org-fragtog)
;; matlab mode, currently only for org mode, could be separated
;; the package name is matlab, yet it provides `matlab'
(use-package matlab
:straight matlab-mode
:diminish
:config
(add-to-list 'auto-mode-alist '("\\.m\\'" . matlab-mode))
(setq matlab-indent-function t)
(setq matlab-shell-command "matlab"))
;; gnuplot mode, currently only for org mode, could be separated
(use-package gnuplot
:diminish
:config
(add-to-list 'auto-mode-alist '("\\.gp$" . gnuplot-mode)))
(use-package ox-hugo
:after ox)
(provide 'init-org)
;;; init-denote.el --- Denote -*- lexical-binding: t -*-
;;; Code:
(use-package denote
:hook
(;; Apply colours to Denote names in Dired. This applies to all
;; directories. Check `denote-dired-directories' for the specific
;; directories you may prefer instead. Then, instead of
;; `denote-dired-mode', use `denote-dired-mode-in-directories'.
(dired-mode . denote-dired-mode))
:bind
:config
(setq denote-directory (expand-file-name "~/notes"))
(setq denote-save-buffers nil)
(setq denote-known-keywords '("emacs" "linux" "openwrt"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-prompts '(title keywords))
(setq denote-excluded-directories-regexp nil)
(setq denote-excluded-keywords-regexp nil)
(setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
;; Pick dates, where relevant, with Org's advanced interface:
(setq denote-date-prompt-use-org-read-date t)
;; By default, we do not show the context of links. We just display
;; file names. This provides a more informative view.
(setq denote-backlinks-show-context t)
;; Automatically rename Denote buffers using the `denote-rename-buffer-format'.
(denote-rename-buffer-mode t))
(use-package consult-notes
:commands (consult-notes
consult-notes-search-in-all-notes
)
:config
(when (locate-library "denote")
(consult-notes-denote-mode))
;; search only for text files in denote dir
(setq consult-notes-denote-files-function (lambda () (denote-directory-files nil t t))))
(global-leader-def
;; denote
"n" '(:ignore t :which-key "Denote")
"nn" #'consult-notes
"nd" #'denote-sort-dired
"nl" #'denote-link
"nL" #'denote-add-links
"nb" #'denote-backlinks
"nr" #'denote-rename-file
"nR" #'denote-rename-file-using-front-matter
)
(provide 'init-denote)
;;; init-latex.el --- Initialize LaTeX settings -*- lexical-binding: t -*-
;; GhostScript is needed for previewing latex fragments
;;; Code:
;; Note that it *must* be 'use-package latex', if 'auctex' is used instead,
;; 'auctex.el' is never called later, and the :config section is not set.
;; Many (most?) people use 'use-package tex', which is fine and probably
;; more "correct", but then care would have to be taken with variables which
;; are not defined in 'tex.el' (starting with "TeX-"), but in 'latex.el'
;; (starting with "LaTeX-"). As 'latex.el' requires 'tex.el', simply setting
;; 'use-package latex' catches all in one go.
(use-package latex
:straight auctex
:config
(add-hook 'LaTeX-mode-hook #'TeX-source-correlate-mode)
(setq
LaTeX-electric-left-right-brace t
TeX-parse-self t ;; parse onload
TeX-auto-save t ;; parse on save
;; use hidden dirs for auctex files
TeX-auto-local ".auctex-auto"
TeX-style-local ".auctex-style"
TeX-source-correlate-method 'synctex
;; don't start the emacs server when correlating sources
TeX-source-correlate-start-server nil
;; just save, dont ask me before each compilation
TeX-save-query nil)
(setq-default TeX-engine 'xetex)
(setq preview-default-option-list '("displaymath" "floats" "graphics" "textmath" "footnotes"))
(setq preview-scale-function 2.0)
(add-to-list 'auto-mode-alist '("\\.tex\\'" . LaTeX-mode)))
(use-package auctex-latexmk
:hook (LaTeX-mode . (lambda ()
(setq TeX-command-default "LatexMk")))
:config
(setq auctex-latexmk-inherit-TeX-PDF-mode t)
(auctex-latexmk-setup))
(provide 'init-latex)
;;; init-dired.el --- Emacs built in file manager -*- lexical-binding: t -*-
;;; Code:
(use-package dired
:straight nil
:config
(setq dired-listing-switches "-alh --group-directories-first"
dired-dwim-target t
dired-create-destination-dirs 'ask
dired-recursive-deletes 'always
dired-recursive-copies 'always))
(use-package nerd-icons-dired
:hook (dired-mode . nerd-icons-dired-mode))
(use-package dired-rsync
:after dired
:config
(general-def dired-mode-map "C-c C-r" #'dired-rsync))
;; Colourful dired
(use-package diredfl
:after dired
:hook (dired-mode . diredfl-mode))
(use-package dired-single
:after dired
:bind
(:map dired-mode-map
([remap dired-find-file] . dired-single-buffer)
([remap dired-mouse-find-file-other-window] . dired-single-buffer-mouse)
([remap dired-up-directory] . dired-single-up-directory)))
(use-package dired-hide-dotfiles
:after dired
:hook (dired-mode . dired-hide-dotfiles-mode)
:config
(general-def dired-mode-map "H" 'dired-hide-dotfiles-mode))
(defun solarion/dired-here ()
"Open current file's directory in Dired."
(interactive)
(dired default-directory))
(general-def "C-c d" #'solarion/dired-here)
(provide 'init-dired)
;;; init-dashboard.el --- Setup for the splash screen (dashboard) -*- lexical-binding: t -*-
;;; Code:
(use-package dashboard
:diminish
:init
(defun solarion-init-time ()
"Showing Emacs initializing time, packages loaded and GC"
(format "Loaded %d packages in %.2f ms."
(- (length load-path) (length (get 'load-path 'initial-value)))
(* 1e3 (float-time (time-subtract after-init-time before-init-time)))))
(defun solarion/dashboard-update-packages ()
(interactive)
(straight-pull-all)
(message "Packages updated."))
(setq dashboard-banner-logo-title (concat "Emacs " emacs-version)
dashboard-startup-banner "~/.emacs.d/logo.png"
dashboard-image-banner-max-height 400
dashboard-page-separator "\n\n"
dashboard-center-content t
dashboard-show-shortcuts t
dashboard-items '((recents . 5)
(bookmarks . 5)
(projects . 3))
dashboard-startupify-list '(dashboard-insert-banner
dashboard-insert-newline
dashboard-insert-banner-title
dashboard-insert-newline
dashboard-insert-navigator
dashboard-insert-newline
dashboard-insert-init-info
dashboard-insert-items)
dashboard-navigator-buttons `(((,(nerd-icons-octicon "nf-oct-history") "Restore (R)" "Restore previous session" (lambda (&rest _) (restore-previous-session)))
(,(nerd-icons-codicon "nf-cod-tools") "Config (C)" "Open custom file" solarion-edit-config)
(,(nerd-icons-mdicon "nf-md-update") "Update (U)" "Update Packages" solarion/dashboard-update-packages))))
(when (< (length command-line-args) 2)
(setq initial-buffer-choice 'dashboard-open))
:config
(dashboard-setup-startup-hook)
(setq dashboard-init-info (solarion-init-time))
(general-def dashboard-mode-map
"R" #'restore-previous-session
"C" #'solarion-edit-config
"U" #'straight-pull-all))
(provide 'init-dashboard)
;;; init-vterm.el --- Emacs libvterm integration -*- lexical-binding: t -*-
;;; Code:
(if (and module-file-suffix ;; dynamic module
(executable-find "cmake")
(executable-find "libtool") ;; install libtool-bin
(executable-find "make"))
(progn
(use-package vterm
:init
(setq vterm-always-compile-module t)
:bind (:map vterm-mode-map
("C-\\" . toggle-input-method)
("C-q" . vterm-send-next-key)))
(use-package vterm-toggle
:bind
([f2] . vterm-toggle)
([C-f2] . vterm-toggle-cd)
(:map vterm-mode-map
("C-<return>" . vterm-toggle-insert-cd)
([f2] . vterm-toggle))))
(unless (get 'solarion/vterm-warning 'shown)
(display-warning 'vterm
"vterm disabled: missing module support or build tools (cmake/libtool/make)."
:warning)
(put 'solarion/vterm-warning 'shown t)))
(provide 'init-vterm)
Restore previous session.
;;; init-persp.el --- Configurations for persp-mode -*- lexical-binding: t -*-
;;; Code:
(use-package persp-mode
:diminish
:hook
((after-init . persp-mode)
(persp-mode . persp-load-frame)
(kill-emacs . persp-save-frame))
:init
(setq persp-keymap-prefix nil
persp-nil-name "default"
persp-set-last-persp-for-new-frames nil
persp-kill-foreign-buffer-behaviour 'kill
persp-auto-resume-time 0)
:config
;; Save and load frame parameters (size & position)
(defvar persp-frame-file (expand-file-name "persp-frame" persp-save-dir)
"File of saving frame parameters.")
(defun persp-save-frame ()
"Save the current frame parameters to file."
(interactive)
(when (and (display-graphic-p) persp-mode)
(condition-case error
(with-temp-buffer
(erase-buffer)
(insert
";;; -*- mode: emacs-lisp; coding: utf-8-unix -*-\n"
";;; This is the previous frame parameters.\n"
";;; Last generated " (current-time-string) ".\n"
"(setq initial-frame-alist\n"
(format " '((top . %d)\n" (frame-parameter nil 'top))
(format " (left . %d)\n" (frame-parameter nil 'left))
(format " (width . %d)\n" (frame-parameter nil 'width))
(format " (height . %d)\n" (frame-parameter nil 'height))
(format " (fullscreen . %s)))\n" (frame-parameter nil 'fullscreen)))
(write-file persp-frame-file))
(error
(warn "persp frame: %s" (error-message-string error))))))
(defun persp-load-frame ()
"Load frame with the previous frame's geometry."
(interactive)
(when (and (display-graphic-p) persp-mode)
(condition-case error
(progn
(load persp-frame-file)
;; Handle multiple monitors gracefully
(when (or (>= (eval (frame-parameter nil 'left)) (display-pixel-width))
(>= (eval (frame-parameter nil 'top)) (display-pixel-height)))
(set-frame-parameter nil 'left 0)
(set-frame-parameter nil 'top 0)))
(error
(warn "persp frame: %s" (error-message-string error))))))
(defun restore-previous-session ()
"Restore the previous session."
(interactive)
(when (bound-and-true-p persp-mode)
(restore-session persp-auto-save-fname))
(message "Restoring previous session from: %s" persp-auto-save-fname))
(defun restore-session (fname)
"Restore the specified session."
(interactive (list (read-file-name "Load perspectives from a file: "
persp-save-dir)))
(when (bound-and-true-p persp-mode)
(message "Restoring session...")
(quit-window t)
(condition-case-unless-debug err
(persp-load-state-from-file fname)
(error "Error: Unable to restore session -- %s" err))
(message "Restoring session...done")))
;; Don't save dead or temporary buffers
(add-hook 'persp-filter-save-buffers-functions
(lambda (b)
"Ignore dead and unneeded buffers."
(or (not (buffer-live-p b))
(string-prefix-p " *" (buffer-name b)))))
(add-hook 'persp-filter-save-buffers-functions
(lambda (b)
"Ignore temporary buffers."
(let ((bname (file-name-nondirectory (buffer-name b))))
(or (string-prefix-p ".newsrc" bname)
(string-prefix-p "magit" bname)
(string-prefix-p "*vterm" bname)
(string-prefix-p "COMMIT_EDITMSG" bname)
(string-prefix-p "Pfuture-Callback" bname)
(string-prefix-p "treemacs-persist" bname)
(string-match-p "\\.elc\\|\\.tar\\|\\.gz\\|\\.zip\\'" bname)
(string-match-p "\\.bin\\|\\.so\\|\\.dll\\|\\.exe\\'" bname)))))
;; Don't save persp configs in `recentf'
(with-eval-after-load 'recentf
(push persp-save-dir recentf-exclude)))
(provide 'init-persp)
;;; init-im.el --- 输入法相关 -*- lexical-binding: t -*-
;;; Code:
;; requires librime-dev on Debian
(use-package rime
:init
(setq default-input-method "rime"
rime-show-candidate 'posframe
rime-popup-style 'vertical
rime-posframe-style 'vertical
rime-user-data-dir (expand-file-name "rime/" user-emacs-directory)
rime-posframe-properties '(:internal-border-width 2))
:config
(general-def rime-mode-map "C-`" #'rime-send-keybinding)
(general-def rime-active-mode-map "S-<delete>" #'rime-send-keybinding))
;; requires emacs-mozc on Debian
(use-package mozc
:init
(setq mozc-candidate-style 'echo-area))
(defun solarion/toggle-input-method ()
"Toggle between nil, rime and japanese-mozc input methods."
(interactive)
(cond
((equal current-input-method nil)
(set-input-method 'rime))
((equal current-input-method "rime")
(set-input-method 'japanese-mozc))
((equal current-input-method "japanese-mozc")
(set-input-method 'rime))))
(general-def :keymaps 'override "C-c SPC" #'solarion/toggle-input-method)
(provide 'init-im)
;;; init-wsl.el --- wsl-specific setup -*- lexical-binding: t -*-
;;; Code:
;; teach Emacs how to open links with your default browser
(let ((cmd-exe "/mnt/c/Windows/System32/cmd.exe")
(cmd-args '("/c" "start")))
(when (file-exists-p cmd-exe)
(setq browse-url-generic-program cmd-exe
browse-url-generic-args cmd-args
browse-url-browser-function 'browse-url-generic
search-web-default-browser 'browse-url-generic)))
;; Fix wayland Copy from wsl to Windows
(setq select-active-regions nil)
(defun wl-copy (text)
(let ((process-connection-type nil))
(let ((proc (start-process "wl-copy" "*Messages*" "wl-copy" "-f" "-n")))
(process-send-string proc text)
(process-send-eof proc))))
(defun wl-paste ()
(shell-command-to-string "wl-paste -n"))
(if (and (executable-find "wl-copy")
(executable-find "wl-paste"))
(progn
(setq interprogram-cut-function 'wl-copy)
(setq interprogram-paste-function 'wl-paste))
(message "wl-clipboard not found; install it (e.g. sudo apt install wl-clipboard) to enable WSL clipboard sync."))
;; WSL tweak to keep Org LaTeX previews scaled correctly under Wayland
(setq display-mm-dimensions-alist '(("wayland-0" . (797 . 344))))
(provide 'init-wsl)