“I’m rarely happier than when spending an entire day programming my computer to perform automatically a task that would otherwise take me a good ten seconds to do by hand.” - Douglas Adams
我用 Doom Emacs(以下简称Doom)作为 Emacs 基本配置。这是一个literate配置文件, 指定了Doom的所有配置并包含了文档。如果将这个文件导入到 ~/.doom.d/config.org
,Doom 会在启动或此文件变动时时自动 tangle 此文件中的代码到对应的文件中。
如果你在浏览器中打开这个文件,你可以考虑在 Emacs Org mode 中打开它以获得更好的阅读体验 :).
在更改各种package.el和init.el后,请记得运行 doom sync
。
这个配置仍在不断优化中,目前启动速度极慢,达 0.7s (在 WSL 中测试)。
- cnsunyour’s Doom Emacs 本配置中中文输入法部分借鉴了此处的写法。
- Lazycat Emacs 本配置中半数插件取材于此。
- Centaur Emacs 本配置中 UI 配置绝大多数由此而来。
- H-cheung’s Doom Emacs 本配置基于此修改而来。
本部分决定了 Doom 所启用的模块和加载顺序。
这是我个人的配置,不客制化,而我已经熟练了 Doom 的快捷键,所以 Dashboard 对我来说并不重要。
在 C++ 中自动格式化不是个好想法,尤其当设置了自动保存时。
;;; init.el -*- lexical-binding: t; -*-
;; This file controls what Doom modules are enabled and what order they load
;; in. Remember to run 'doom sync' after modifying it!
;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
;; documentation. There you'll find a link to Doom's Module Index where all
;; of our modules are listed, including what flags they support.
;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
;; 'C-c c k' for non-vim users) to view its documentation. This works on
;; flags as well (those symbols that start with a plus).
;;
;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; directory (for easy access to its source code).
(setq initial-frame-alist '((top . 0.5)
(left . 0.5)
(width . 0.628)
(height . 0.8)
(fullscreen)))
(doom! :input
;;bidi ; (tfel ot) thgir etirw uoy gnipleh
;;chinese
;;japanese
;;layout ; auie,ctsrnm is the superior home row
:completion
;;(company +childframe) ; the ultimate code completion backend
(corfu +icons +orderless +dabbrev) ; complete with cap(f), cape and a flying feather!
;;helm ; the *other* search engine for love and life
;;ido ; the other *other* search engine...
;;ivy ; a search engine for love and life
(vertico +icons +childframe) ; the search engine of the future
:ui
;;deft ; notational velocity for Emacs
doom ; what makes DOOM look the way it does
;;doom-dashboard ; a nifty splash screen for Emacs
;;doom-quit ; DOOM quit-message prompts when you quit Emacs
;;(emoji +ascii +github) ; 🙂
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
indent-guides ; highlighted indent columns
;; ligatures ; ligatures and symbols to make your code pretty again
;; minimap ; show a map of the code on the side
modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink cursor line after big motions
;;neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;tabs ; a tab bar for Emacs
treemacs ; a project drawer, like neotree but cooler
unicode ; extended unicode support for various languages
(vc-gutter +pretty) ; vcs diff in the fringe
;;vi-tilde-fringe ; fringe tildes to mark beyond EOB
;; window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces
zen ; distraction-free coding or writing
:editor
meow ; come to the dark side, we have cookies
file-templates ; auto-snippets for empty files
fold ; (nigh) universal code folding
format ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who don't like vim
multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of
;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to
;;word-wrap ; soft wrapping with language-aware indent
:emacs
(dired +icons) ; making dired pretty [functional]
;;electric ; smarter, keyword-based electric-indent
;;eww ; the internet is gross
(ibuffer +icons) ; interactive buffer management
undo ; persistent, smarter undo for your inevitable mistakes
vc ; version-control and Emacs, sitting in a tree
:term
eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for Emacs
vterm ; the best terminal emulation in Emacs
:checkers
(syntax +childframe +flymake) ; tasing you for every semicolon you forget
;;(spell +flyspell) ; tasing you for misspelling mispelling
;;grammar ; tasing grammar mistake every you make
:tools
;;ansible
;;biblio ; Writes a PhD for you (citation needed)
;;collab ; buffers with friends
debugger ; FIXME stepping through code, to help you add bugs
;;direnv
;;docker
editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
eval ; run code, run (also, repls)
lookup ; navigate your code and its documentation
(lsp +eglot) ; M-x vscode
magit ; a git porcelain for Emacs
;;make ; run make tasks from Emacs
;;pass ; password manager for nerds
;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
;;tree-sitter ; syntax and parsing, sitting in a tree...
;;upload ; map local to remote projects via ssh/ftp
:os
(:if (featurep :system 'macos) macos) ; improve compatibility with macOS
;; (tty +osc) ; improve the terminal Emacs experience
:lang
;;agda ; types of types of types of types...
;;beancount ; mind the GAAP
;;(cc +lsp) ; C > C++ == 1
;;clojure ; java with a lisp
;;common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs
;;crystal ; ruby at the speed of c
;;csharp ; unity, .NET, and mono shenanigans
;;data ; config/data formats
;;(dart +flutter) ; paint ui and not much else
;;dhall
;;elixir ; erlang done right
;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses
;;erlang ; an elegant language for a more civilized age
;;ess ; emacs speaks statistics
;;factor
;;faust ; dsp, but you get to keep your soul
;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER)
;;fsharp ; ML stands for Microsoft's Language
;;fstar ; (dependent) types and (monadic) effects and Z3
;;gdscript ; the language you waited for
;;(go +lsp) ; the hipster dialect
;;(graphql +lsp) ; Give queries a REST
;;(haskell +lsp) ; a language that's lazier than I am
;;hy ; readability of scheme w/ speed of python
;;idris ; a language you can depend on
;;json ; At least it ain't XML
;;(java +lsp) ; the poster child for carpal tunnel syndrome
;;javascript ; all(hope(abandon(ye(who(enter(here))))))
;;julia ; a better, faster MATLAB
;;kotlin ; a better, slicker Java(Script)
latex ; writing papers in Emacs has never been so fun
;;lean ; for folks with too much to prove
;;ledger ; be audit you can be
;;lua ; one-based indices? one-based indices
(markdown +grip) ; writing docs for people to ignore
;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(org +pretty) ; organize your plain life in plain text
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
;;graphviz ; diagrams for confusing yourself even more
;;purescript ; javascript, but functional
;;python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client
;;rst ; ReST in peace
;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;(rust +lsp) ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good
;;(scheme +guile) ; a fully conniving family of lisps
;;sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
;;web ; the tubes
;;yaml ; JSON, but readable
;;zig ; C, but simpler
:app
calendar
;;emms
;;everywhere ; *leave* Emacs!? You must be joking
;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
:email
;;(mu4e +org +gmail)
;;notmuch
;;(wanderlust +gmail)
:config
literate
;;use-package
(default +bindings)
:baosize
chinese
;;awesome-tray
holo-layer
eaf
popweb
;;blink-search
sort-tab
corfu-english-helper
color-rg
;;nyan
treesit-context
symbol-overlay
winum
super-save
;; keyfreq
;; emacs-lsp-booster
colorful-mode
;; avy-thing-edit
super-eglot
vterm-run
dape)
;;; config.el -*- lexical-binding: t; -*-
85%真透明(只透明背景,不透明文字)
(add-to-list 'default-frame-alist '(alpha-background . 85))
nord 主题
(setq doom-theme 'doom-nord-light)
VictorMono & 霞鹜文楷,emoji设置特殊字体
(setq doom-font (font-spec :family "VictorMono Nerd Font" :size 14 :weight 'Regular)
doom-symbol-font (font-spec :family "LXGW WenKai Mono GB" :size 14 :weight 'Regular)
doom-variable-pitch-font (font-spec :family "LXGW WenKai Mono GB" :size 14 :weight 'Regular)
doom-big-font (font-spec :family "VictorMono Nerd Font" :size 16 :weight 'Regular)
nerd-icons-font-family "VictorMono Nerd Font")
(defun +font-set-emoji (&rest _)
(set-fontset-font t 'emoji "Noto Color Emoji" nil 'prepend))
(add-hook 'after-setting-font-hook #'+font-set-emoji)
平滑滚动
(when (display-graphic-p)
(setq mouse-wheel-scroll-amount '(1 ((shift) . hscroll))
mouse-wheel-scroll-amount-horizontal 1
mouse-wheel-progressive-speed nil))
(setq scroll-step 1
scroll-margin 0
scroll-conservatively 100000
auto-window-vscroll nil
scroll-preserve-screen-position t)
(if (fboundp 'pixel-scroll-precision-mode)
(pixel-scroll-precision-mode t)
(unless sys/macp
(use-package good-scroll
:diminish
:hook (after-init . good-scroll-mode)
:bind (([remap next] . good-scroll-up-full-screen)
([remap prior] . good-scroll-down-full-screen)))))
Centaur Emacs 中的一些基础设置
(use-package simple
:hook ((after-init . size-indication-mode)
(text-mode . visual-line-mode)
((prog-mode markdown-mode conf-mode) . enable-trailing-whitespace))
:init
(setq column-number-mode t
line-number-mode t
kill-whole-line t ; Kill line including '\n'
line-move-visual nil
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
;; Visualize TAB, (HARD) SPACE, NEWLINE
(setq-default show-trailing-whitespace nil) ; Don't show trailing whitespace by default
(defun enable-trailing-whitespace ()
"Show trailing spaces and delete on saving."
(setq show-trailing-whitespace t)
(add-hook 'before-save-hook #'delete-trailing-whitespace nil t))
;; Prettify the process list
(with-no-warnings
(defun my-list-processes--prettify ()
"Prettify process list."
(when-let* ((entries tabulated-list-entries))
(setq tabulated-list-entries nil)
(dolist (p (process-list))
(when-let* ((val (cadr (assoc p entries)))
(name (aref val 0))
(pid (aref val 1))
(status (aref val 2))
(status (list status
'face
(if (memq status '(stop exit closed failed))
'error
'success)))
(buf-label (aref val 3))
(tty (list (aref val 4) 'face 'font-lock-doc-face))
(thread (list (aref val 5) 'face 'font-lock-doc-face))
(cmd (list (aref val 6) 'face 'completions-annotations)))
(push (list p (vector name pid status buf-label tty thread cmd))
tabulated-list-entries)))))
(advice-add #'list-processes--refresh :after #'my-list-processes--prettify)))
Treesitter 默认的高亮太素了,但是定义多了影响速度,设置一下jit-lock-defer-time
(setq treesit-font-lock-level 4
major-mode-remap-alist
'((yaml-mode . yaml-ts-mode)
(sh-mode . bash-ts-mode)
(js-mode . js-ts-mode)
(css-mode . css-ts-mode)
(c-mode . c-ts-mode)
(c++-mode . c++-ts-mode)
(c-or-c++-mode . c-or-c++-ts-mode)
(python-mode . python-ts-mode)))
(defun my-fontify-variable (node override start end &rest _)
(let ((parent (treesit-node-parent node)) tyn)
(catch 'break
(while parent
(setq tyn (treesit-node-type parent))
(cond ((or (equal tyn "call_expression") (equal tyn "template_function"))
(progn
(treesit-fontify-with-override (treesit-node-start node) (treesit-node-end node) 'font-lock-function-call-face override start end)
(throw 'break nil))))
(cond ((or (equal tyn "argument_list") (equal tyn "field_expression")) (progn (setq parent nil) (throw 'break nil))))
(cond (t (setq parent (treesit-node-parent parent))))))
(when (not parent) (treesit-fontify-with-override (treesit-node-start node) (treesit-node-end node) 'font-lock-variable-use-face override start end))))
(advice-add 'c-ts-mode--fontify-variable :around (lambda (fn &rest args) (eval `(my-fontify-variable ,@args))))
(defun my-c-font-lock-settings (fn mode)
(if (eq mode 'cpp)
`(
,@(treesit-font-lock-rules
:language 'cpp
:feature 'function
'((destructor_name (identifier) @font-lock-function-name-face))
;; :language mode
;; :feature 'property
;; '((template_method (field_identifier) @font-lock-function-call-face))
)
,@(funcall fn mode))
(funcall fn mode)))
(advice-add 'c-ts-mode--font-lock-settings :around 'my-c-font-lock-settings)
;; (add-hook 'meow-insert-mode-hook (lambda () (setq jit-lock-defer-time 0.25)))
;; (add-hook 'meow-normal-mode-hook (lambda () (setq jit-lock-defer-time 0)))
C++ 缩进和调试
(add-hook 'c++-ts-mode-hook (lambda ()
(setq c-basic-offset 4)
(rainbow-delimiters-mode-enable)
(lsp!)
(treesit-context)
(bind-key "C-c d c" #'cpp-gdb 'c++-ts-mode-map)
(defun cpp-gdb ()
"open compile and C++ debug"
(interactive)
(if buffer-file-name
(let ((filename (file-name-sans-extension (file-name-nondirectory buffer-file-name))))
(when (eq 0 (shell-command (concat "g++ -g3 -std=c++17 " buffer-file-name " -o /tmp/cpp-" filename)))
(gdb (concat "gdb -i=mi /tmp/cpp-" filename))))
(message "buffer-file-name is nil")))))
键绑定
(bind-keys ("C-c f o" . consult-org-agenda)
("C-s" . consult-line))
由于本人使用fish作为默认shell所以要做一点操作才行
(setq shell-file-name (executable-find "bash"))
(setq-default vterm-shell (executable-find "fish"))
(setq-default explicit-shell-file-name (executable-find "fish"))
显示时间
(use-package time
:init (setq display-time-default-load-average nil
display-time-format "%H:%M"))
(display-time-mode)
让符号更漂亮
(setq-default prettify-symbols-alist
'(("[ ]" . ?)
("[-]" . ?)
("[X]" . ?)
(":PROPERTIES:" . ?)
(":ID:" . ?🪪)
(":END:" . ?🔚)
("#+ARCHIVE:" . ?📦)
("#+AUTHOR:" . ?👤)
("#+CREATOR:" . ?💁)
("#+DATE:" . ?📆)
("#+DESCRIPTION:" . ?⸙)
("#+EMAIL:" . ?📧)
("#+HEADERS" . ?☰)
("#+OPTIONS:" . ?⚙)
("#+SETUPFILE:" . ?⚒)
("#+TAGS:" . ?🏷)
("#+TITLE:" . ?📓)
("#+BEGIN_SRC" . ?✎)
("#+END_SRC" . ?□)
("#+BEGIN_QUOTE" . ?«)
("#+END_QUOTE" . ?»)
("#+RESULTS:" . ?💻)
("lambda" . ?λ)
("<-" . ?←)
("->" . ?→)
("->>" . ?↠)
("=>" . ?⇒)
("map" . ?↦)
("/=" . ?≠)
("!=" . ?≠)
("==" . ?≡)
("<=" . ?≤)
(">=" . ?≥)
("=<<" . (?= (Br . Bl) ?≪))
(">>=" . (?≫ (Br . Bl) ?=))
("<=<" . ?↢)
(">=>" . ?↣)
("&&" . ?∧)
("||" . ?∨)
("not" . ?¬)))
(setq prettify-symbols-unprettify-at-point 'right-edge)
(global-prettify-symbols-mode 1)
(add-hook 'prog-mode-hook #'prettify-symbols-mode)
(add-hook 'org-mode-hook #'+org-pretty-mode)
彩色缩进
(use-package indent-bars
:custom
(indent-bars-color '(highlight :face-bg t :blend 0.225))
(indent-bars-treesit-support t)
(indent-bars-pattern ".")
(indent-bars-no-descend-string t)
(indent-bars-color-by-depth '(:regexp "outline-\\([0-9]+\\)" :blend 1))
(indent-bars-highlight-current-depth t)
(indent-bars-pattern "|")
(indent-bars-display-on-blank-lines nil)
(indent-bars-treesit-ignore-blank-lines-types '("module"))
(indent-bars-prefer-character t)
(indent-bars-treesit-scope '((python function_definition class_definition for_statement
if_statement with_statement while_statement)))
:hook ((prog-mode yaml-mode) . indent-bars-mode)
:config (require 'indent-bars-ts))
支持 FooBar 类 subword 移动
(global-subword-mode)
设置括号匹配,用绿色高亮
(use-package paren
:custom-face
(show-paren-match ((((class color) (background light))
(:box (:line-width (-1 . -1) :color "gray70")))
(((class color) (background dark))
(:box (:line-width (-1 . -1) :color "gray50")))))
:hook (after-init . show-paren-mode)
:init (setq show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t)
:config
(setq blink-matching-paren-highlight-offscreen t
show-paren-context-when-offscreen 'child-frame))
设置 doom-modeline
(setq doom-modeline-major-mode-icon t
doom-modeline-major-mode-color-icon t)
打开面包屑导航
(when (modulep! :tools lsp +lsp)
(add-hook 'lsp-mode-hook #'lsp-headerline-breadcrumb-mode))
悬浮窗口
corfu 貌似不应该出现在这里,但是不放这里就没法工作
(standard-display-unicode-special-glyphs) ; 终端中的弹窗不设置会使用ASCII边框
(use-package posframe
:init
(defface posframe-border
`((t (:inherit region)))
"Face used by the `posframe' border."
:group 'posframe)
(defvar posframe-border-width 2
"Default posframe border width.")
:config
(with-no-warnings
(defun my-posframe--prettify-frame (&rest _)
(set-face-background 'fringe nil posframe--frame))
(advice-add #'posframe--create-posframe :after #'my-posframe--prettify-frame)
(defun posframe-poshandler-frame-center-near-bottom (info)
(cons (/ (- (plist-get info :parent-frame-width)
(plist-get info :posframe-width))
2)
(/ (+ (plist-get info :parent-frame-height)
(* 2 (plist-get info :font-height)))
2)))))
(use-package! org-modern
:after org
:config
(add-hook 'org-mode-hook #'org-modern-mode))
(use-package transient-posframe
:defer 2
:defines posframe-border-width
:custom-face
(transient-posframe ((t (:inherit tooltip))))
(transient-posframe-border ((t (:inherit posframe-border :background unspecified))))
:init
(setq transient-posframe-border-width posframe-border-width
transient-posframe-min-width 80
transient-posframe-min-height nil
transient-posframe-poshandler 'posframe-poshandler-point-frame-center
transient-posframe-parameters '((left-fringe . 8)
(right-fringe . 8)))
:config
(with-no-warnings
;; FIXME:https://github.com/yanghaoxie/transient-posframe/issues/5#issuecomment-1974871665
(defun my-transient-posframe--show-buffer (buffer _alist)
"Show BUFFER in posframe and we do not use _ALIST at this period."
(when (posframe-workable-p)
(let* ((posframe
(posframe-show buffer
:font transient-posframe-font
:position (point)
:poshandler transient-posframe-poshandler
:background-color (face-attribute 'transient-posframe :background nil t)
:foreground-color (face-attribute 'transient-posframe :foreground nil t)
:initialize #'transient-posframe--initialize
:min-width transient-posframe-min-width
:min-height transient-posframe-min-height
:internal-border-width transient-posframe-border-width
:internal-border-color (face-attribute 'transient-posframe-border :background nil t)
:override-parameters transient-posframe-parameters)))
(frame-selected-window posframe))))
(advice-add #'transient-posframe--show-buffer :override #'my-transient-posframe--show-buffer)
(setq transient-mode-line-format nil) ; without line
(defun transient-posframe--initialize ()
"Initialize transient posframe."
(setq window-resize-pixelwise t)
(setq window-size-fixed nil))
(defun transient-posframe--resize (window)
"Resize transient posframe."
(fit-frame-to-buffer-1 (window-frame window)
nil transient-posframe-min-height
nil transient-posframe-min-width))
(advice-add 'transient--fit-window-to-buffer :override #'transient-posframe--resize)
(defun my-transient-posframe--hide ()
"Hide transient posframe."
(posframe-hide transient--buffer-name))
(advice-add #'transient-posframe--delete :override #'my-transient-posframe--hide))
(transient-posframe-mode 1))
(setq vertico-posframe-poshandler #'posframe-poshandler-point-window-center
vertico-posframe-parameters '((left-fringe . 8)(right-fringe . 8)))
(with-eval-after-load 'xref
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref))
(with-eval-after-load 'corfu
(global-corfu-mode)
(bind-keys :map corfu-map
("C-SPC" . corfu-insert-separator)
("C-n" . corfu-next)
("C-p" . corfu-previous)
("M-p" . corfu-popupinfo-scroll-up)
("M-n" . corfu-popupinfo-scroll-down)
("M-d" . corfu-popupinfo-toggle)
("RET" . corfu-insert)
("C-x C-k" . cape-dict)
("C-x C-f" . cape-file)))
简化提示,用 y/n 代替 yes/no ,别再提醒我 “Really kill emacs?” 了。
从 manateelazycat 大佬的配置上抄的,但我并不知道新语法 advice-add 怎么用。
(fset 'yes-or-no-p 'y-or-n-p)
;; (advice-add 'save-buffer-kill-emacs :around (lambda (fn &rest)
;; (require 'noflet)
;; (setq confirm-kill-emacs nil)
;; (noflet (process-list) ad-do-it)))
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
"Prevent annoying \"Active processes exist\" query when you quit Emacs."
(require 'noflet)
(setq confirm-kill-emacs nil)
(noflet (process-list) ad-do-it))
自定义Variables和Faces
我不知道这是干嘛用的,但它既然在这里……
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
(custom-set-faces)
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
Emacs 核心所需的插件
;; -*- no-byte-compile: t; -*-
;;; packages.el
(unpin! t)
;;(package! vc-msg)
;;(package! power-mode)
;;(package! imenu-list)
(package! org-modern)
(package! noflet)
(package! transient-posframe)
;;(package! cal-china-x)
;;(package! railgun :recipe(:host github :repo "gynamics/railgun.el"))
Evil实在是太重了,但我又无法适应Emacs原生按键,就使用轻量级的Meow了
M-x meow-tutor
以学习Meow按键(类似于vim-tutor)
;; -*- no-byte-compile: t; -*-
;;; modules/editor/meow/packages.el
(package! meow)
;;; modules/editor/meow/config.el -*- lexical-binding: t; -*-
(defun meow/setup ()
(setq meow-use-cursor-position-hack t
meow-use-clipboard t
meow-use-enhanced-selection-effect t)
(bind-keys :map meow-normal-state-keymap
("0" . meow-expand-0)
("1" . meow-expand-1)
("2" . meow-expand-2)
("3" . meow-expand-3)
("4" . meow-expand-4)
("5" . meow-expand-5)
("6" . meow-expand-6)
("7" . meow-expand-7)
("8" . meow-expand-8)
("9" . meow-expand-9)
("-" . negative-argument)
(";" . meow-reverse)
("," . meow-inner-of-thing)
("." . meow-bounds-of-thing)
("'" . repeat)))
(defun meow-append-this-line ()
(interactive)
(move-end-of-line 1)
(meow-insert))
(defun meow-insert-this-line ()
(interactive)
(move-beginning-of-line 1)
(meow-insert))
(defun meow/setup-qwerty ()
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
(meow/setup)
(bind-keys :map meow-normal-state-keymap
("[" . meow-beginning-of-thing)
("]" . meow-end-of-thing)
("a" . meow-append)
("o" . meow-open-below)
("A" . meow-append-this-line)
("b" . meow-back-word)
("B" . meow-back-symbol)
("c" . meow-change)
("e" . meow-next-word)
("E" . meow-next-symbol)
("f" . meow-find)
("g" . meow-cancel-selection)
("G" . meow-grab)
("h" . meow-left)
("H" . meow-left-expand)
("i" . meow-insert)
("I" . meow-insert-this-line)
("O" . meow-open-above)
("j" . meow-next)
("J" . meow-next-expand)
("k" . meow-prev)
("K" . meow-prev-expand)
("l" . meow-right)
("L" . meow-right-expand)
("v" . meow-visit)
("m" . meow-join)
("n" . meow-search)
("%" . meow-block)
("p" . meow-yank)
("q" . meow-quit)
("Q" . meow-goto-line)
("r" . meow-replace)
("R" . meow-swap-grab)
("d" . meow-kill)
("t" . meow-till)
("u" . meow-undo)
("U" . meow-undo-in-selection)
("/" . meow-comment)
("w" . meow-mark-word)
("W" . meow-mark-symbol)
("x" . meow-line)
("X" . meow-goto-line)
("y" . meow-save)
("Y" . meow-sync-grab)
("z" . meow-pop-selection)))
(use-package meow
:hook (doom-after-modules-config . meow-global-mode)
:demand t
:config
(meow/setup-qwerty)
(bind-keys :map meow-keymap ([remap describe-key] . helpful-key))
(meow-define-keys
'normal
'("s" . avy-goto-char)
'("F" . avy-goto-char-2)))
懒猫的底部状态栏,代替 modeline
,与 sort-tab
一样以最小窗口空间占用为理念。
;;; modules/baosize/awesome-tray/config.el -*- lexical-binding: t; -*-
显示 lsp-bridge
状态和诊断数目
(defun awesome-tray-lsp-module () (if (not (equal lsp-bridge-mode nil))
(if (not (equal lsp-bridge-diagnostic-count nil))
(concat " " (int-to-string lsp-bridge-diagnostic-count))
" ")
""))
(defface awesome-tray-module-lsp-face
'((((background light)) :inherit awesome-tray-orange-face)
(t :inherit awesome-tray-orange-face))
"Lsp-bridge face."
:group 'awesome-tray)
显示当前光标所在函数
(defun awesome-tray-mybelong-module ()
(let ((origin (if (modulep 'treesit)
(let ((current-seconds (awesome-tray-current-seconds)))
(if (or (not (eq (current-buffer) awesome-tray-belong-last-buffer))
(> (- current-seconds awesome-tray-belong-last-time) awesome-tray-belong-update-duration))
(progn
(setq awesome-tray-belong-last-time current-seconds)
(setq awesome-tray-belong-last-buffer (current-buffer))
(awesome-tray-update-belong-cache))
awesome-tray-belong-cache))"")))
(if (equal origin "") "" (concat " " origin))))
显示 meow
状态
(defun awesome-tray-mymeow-module ()
(let ((origin (with-demoted-errors
""
(if (and (modulep 'meow) awesome-tray-meow-show-mode)
meow--indicator
""))))
(concat "" origin)))
显示 Git
状态
(defun awesome-tray-mygit-module ()
(let ((origin (if (executable-find "git")
(progn
(if (not (string= (buffer-file-name) awesome-tray-git-buffer-filename))
(awesome-tray-git-command-update-cache))
awesome-tray-git-command-cache)
"")))
(if (equal origin "") "" (concat " " origin))))
添加上述模块到 awesome-tray
核心并挂上启动时的钩子
(use-package 'awesome-tray
:defer t
:hook (doom-after-init . awesome-tray-mode)
:custom
(awesome-tray-buffer-read-only-style " ")
(awesome-tray-mode-line-active-color "#4ea9e6")
(awesome-tray-belong-update-duration 1)
(awesome-tray-active-modules '("winum"
"lsp"
"input-method"
"mybelong"
"mymeow"
"file-path"
"buffer-read-only"
"mygit"
""
"date"
"clock"))
(awesome-tray-input-method-local-style "㞢")
:config
(add-to-list 'awesome-tray-module-alist
'("winum" . (awesome-tray-winum-module awesome-tray-winum-module-face)))
(add-to-list 'awesome-tray-module-alist
'("mybelong" . (awesome-tray-mybelong-module awesome-tray-module-belong-face)))
(add-to-list 'awesome-tray-module-alist
'("mymeow" . (awesome-tray-mymeow-module awesome-tray-module-meow-face)))
(add-to-list 'awesome-tray-module-alist
'("mygit" . (awesome-tray-mygit-module awesome-tray-module-git-face)))
(add-to-list 'awesome-tray-module-alist
'("lsp" . (awesome-tray-lsp-module awesome-tray-module-lsp-face))))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/awesome-tray/packages.el
(package! awesome-tray
:recipe (:host github :repo "manateelazycat/awesome-tray"))
懒猫的多源搜索,据说很快就可以取代 vertico+consult
全家桶了。
;;; modules/baosize/blink-search/config.el -*- lexical-binding: t; -*-
(use-package blink-search
由于某种原因, blink-search
不能正常加载,需要指定 load-path
。
注意如果把 Doom 安装在 ~/.config/emacs
需要更改位置。
:load-path "~/.config/doomemacs/.local/straight/repos/blink-search/"
绑定键位就使用默认的 C-M-g
吧。
:bind (("C-M-g" . blink-search))
进入 blink-search
时肯定得是 meow-insert-mode
啊
:config (add-hook 'blink-search-mode-hook #'meow-insert))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/blink-search/packages.el
(package! blink-search
:recipe (:host github :repo "manateelazycat/blink-search"))
懒猫的搜索插件,类似于 el-search
,但是更易用,更快
;;; modules/baosize/color-rg/config.el -*- lexical-binding: t; -*-
(use-package color-rg
:bind
(("C-c r i" . color-rg-search-input)
("C-c r s" . color-rg-search-symbol)
("C-c r I" . color-rg-search-input-in-project)
("C-c r S" . color-rg-search-symbol-in-project)
("C-c r b" . color-rg-search-input-in-current-file)
("C-c r j" . color-rg-search-symbol-in-current-file)
("C-c r t" . color-rg-search-project-with-type)
("C-c r x" . color-rg-search-symbol-with-type)))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/color-rg/packages.el
(package! color-rg
:recipe (:host github :repo "manateelazycat/color-rg"))
Emacs Application Frames
,由懒猫开发的使 “Live in Emacs” 成为现实的超级应用框架,也是本配置的核心之一。
;;; modules/baosize/eaf/config.el -*- lexical-binding: t; -*-
判断是否是终端,是则不加载 EAF 以节省启动时间
(when (and (display-graphic-p) (not (daemonp)))
启动 EAF 框架
(use-package eaf
:after-call doom-after-init-hook
:hook
(eaf-mode . doom-modeline-mode)
:init
(bind-keys ("C-c ee" . eaf-open-this-buffer)
("C-c ef" . eaf-open)
("C-c em" . eaf-open-bookmark)))
启动浏览器(这么大一个包,肯定得懒加载)
(use-package eaf-browser
自定义外观
:custom
;;eaf-browser-dark-mode t
(eaf-browser-default-search-engine "bing")
(eaf-webengine-font-family "VictorMono Nerd Font")
(eaf-webengine-fixed-font-family "VictorMono Nerd Font")
(eaf-webengine-serif-font-family "VictorMono Nerd Font")
(eaf-webengine-font-size 16)
(eaf-webengine-fixed-font-size 16)
自定义搜索引擎
(eaf-browser-search-engines '(("bing" . "https://bing.com/search?q=%s"))
("baidu" . "https://www.baidu.com/search?ie=utf-8&q=%s")
("google" . "http://www.google.com/search?ie=utf-8&q=%s")
("duckduckgo" . "https://duckduckgo.com/?q=%s"))
设置为默认浏览器
(browse-url-browser-function #'eaf-open-browser)
ESC 退出焦点
:config
(eaf-bind-key clear_focus "<escape>" eaf-browser-keybinding)
洛谷小插件😅
(defun luogu-open-problem (pid)
"打开题目"
(interactive "M[Luogu] ProblemID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/problem/" pid)))
(defun luogu-open-discuss (did)
"打开讨论"
(interactive "M[Luogu] DiscussID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/discuss/" did)))
(defun luogu-open-training (tid)
"打开题单"
(interactive "M[Luogu] TrainingID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/training/" tid)))
(defun luogu-open-user-home (uid)
"打开用户主页"
(interactive "M[Luogu] UserID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/user/" uid)))
(defun luogu-open-contest (cid)
"打开比赛"
(interactive "M[Luogu] ContestID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/contest/" cid)))
(defun luogu-open-team (teamid)
"打开团队"
(interactive "M[Luogu] TeamID: ")
(eaf-open-browser (concat "https://www.luogu.com.cn/team/" teamid)))
浏览器键绑定
:bind (("C-c e b" . eaf-open-browser)
("C-c e h" . eaf-open-browser-with-history)
("C-c e B" . eaf-open-browser-other-window)
("C-c e s" . eaf-open-browser-same-window)
("C-c elc" . luogu-open-contest)
("C-c eld" . luogu-open-discuss)
("C-c ele" . luogu-open-team)
("C-c elp" . luogu-open-problem)
("C-c elt" . luogu-open-training)
("C-c elu" . luogu-open-user-home)))
启动终端
(use-package eaf-pyqterminal
设置外观
:custom
(eaf-pyqterminal-font-size 16)
(eaf-pyqterminal-font-family "VictorMono Nerd Font")
终端键绑定
:bind (("C-c e t" . eaf-open-pyqterminal)
("C-c e i" . eaf-open-ipython)))
文件管理器键绑定
(use-package eaf-file-manager
:bind (("C-c e /" . eaf-open-in-file-manager)))
启动预览
(use-package eaf-org-previewer
:after-call org-mode)
(use-package eaf-markdown-previewer
:after-call (markdown-mode gfm-mode))
目前存在没修好的 bug 的一些包
(with-eval-after-load 'eaf
(use-package eaf-pdf-viewer))
;; (use-package eaf-git :bind (("C-c e g" . eaf-open-git)))
)
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/eaf/packages.el
eaf 需要编译相关依赖
(defun +eaf-install-deps-for-app(app-dir)
"Install deps from dependencies.json."
(let* ((deps-dict (with-temp-buffer
(insert-file-contents
(expand-file-name "dependencies.json" app-dir))
(json-parse-string (buffer-string))))
(pip-deps (gethash (if IS-LINUX "linux" "darwin")
(or (gethash "pip" deps-dict)
(make-hash-table))))
(vue-install (gethash "vue_install" deps-dict))
(npm-install (gethash "npm_install" deps-dict))
(npm-rebuild (gethash "npm_rebuild" deps-dict)))
(when pip-deps
(dolist (pkg (append pip-deps nil))
(message "%s" (shell-command-to-string (format "pip install %s" pkg)))))
(when vue-install
(let ((default-directory app-dir))
(message "%s" (shell-command-to-string "npm install"))
(message "%s" (shell-command-to-string "npm run build"))))
(when npm-install
(let ((default-directory app-dir))
(message "%s" (shell-command-to-string "npm install"))))
(when npm-rebuild
(let ((default-directory app-dir))
(message "%s" (shell-command-to-string "npm rebuild"))))))
安装 eaf 核心
(package! eaf
:recipe (:host github :repo "emacs-eaf/emacs-application-framework"
:files ("*")
:post-build
(shell-command "python install-eaf.py --install-core-deps")))
安装浏览器
(package! eaf-browser
:recipe (:host github :repo "emacs-eaf/eaf-browser"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-browser"))))
安装终端
(package! eaf-pyqterminal
:recipe (:host github :repo "mumu-lhl/eaf-pyqterminal"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-pyqterminal"))))
安装文件管理器
(package! eaf-file-manager
:recipe (:host github :repo "emacs-eaf/eaf-file-manager"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-file-manager"))))
安装预览插件
(package! eaf-org-previewer
:recipe (:host github :repo "emacs-eaf/eaf-org-previewer"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-org-previewer"))))
(package! eaf-markdown-previewer
:recipe (:host github :repo "emacs-eaf/eaf-markdown-previewer"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-markdown-previewer"))))
一些有 bug 的包
(package! eaf-pdf-viewer
:recipe (:host github :repo "emacs-eaf/eaf-pdf-viewer"
:files ("*")
:post-build
(+eaf-install-deps-for-app
(concat straight-base-dir "/straight/" straight-build-dir "/eaf-pdf-viewer"))))
;;(package! eaf-git
;; :recipe (:host github :repo "emacs-eaf/eaf-git"
;; :files ("*")
;; :post-build
;; (+eaf-install-deps-for-app
;; (concat straight-base-dir "/straight/" straight-build-dir "/eaf-git"))))
懒猫开发的各种特效,没有准 Linux
环境所以目前唯一成功的环境是 wslg+sway
。
这个模块会破坏 blink-search
的窗口,不建议启用。
;;; modules/baosize/holo-layer/config.el -*- lexical-binding: t; -*-
(when (and (display-graphic-p) (not (daemonp)))
解决找不到函数的 bug
(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/blink-search/backend/")
启动 holo-layer
(use-package holo-layer
:custom
(holo-layer-enable-cursor-animation t)
(holo-layer-enable-type-animation t)
(holo-layer-cursor-animation-interval 3)
(holo-layer-cursor-animation-duration 50)
(holo-layer-enable-window-border t)
;; (holo-layer-enable-place-info t)
(holo-layer-enable-window-number-background t)
;; (holo-layer-hide-mode-line t)
;; (holo-layer-enable-indent-rainbow t)
(holo-layer-cursor-color nil)
(holo-layer-cursor-alpha 255)
(holo-layer-type-animation-style "supernova")
(holo-layer-sort-tab-ui t)
:config
(holo-layer-enable)))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/holo-layer/packages.el
(package! holo-layer
:recipe (:host github :repo "manateelazycat/holo-layer"
:files ("*")
:build (:not compile)))
懒猫的标签栏,启用 holo-layer
即可显示图标
;;; modules/baosize/sort-tab/config.el -*- lexical-binding: t; -*-
(use-package sort-tab
:defer t
:hook
(doom-after-init . sort-tab-mode)
:init
(bind-keys ("C-c b]" . sort-tab-select-next-tab)
("C-c bn" . sort-tab-select-next-tab)
("C-<tab>" . sort-tab-select-next-tab)
("C-<iso-lefttab>" . sort-tab-select-prev-tab)
("C-c b[" . sort-tab-select-prev-tab)
("C-c bp" . sort-tab-select-prev-tab)
("C-c bl" . sort-tab-select-last-tab)
("C-c bK" . sort-tab-close-all-tabs)
("C-c bO" . sort-tab-close-other-tabs)
("C-c bd" . sort-tab-close-current-tab)
("C-c bk" . sort-tab-close-current-tab)))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/sort-tab/packages.el
(package! sort-tab
:recipe (:host github :repo "manateelazycat/sort-tab"))
懒猫开发的多媒体弹窗,目前支持预览 LaTeX
,弹出各种翻译。
load-path
;;; modules/baosize/popweb/config.el -*- lexical-binding: t; -*-
(when (and (display-graphic-p) (not (daemonp)))
;;(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/org-roam")
(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/latex")
(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/dict")
;;(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/color-picker")
;;(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/anki-review")
(add-to-list 'load-path "~/.config/doomemacs/.local/straight/repos/popweb/extension/url-preview")
加载对应应用
(use-package popweb
:defer t
:bind
("C-c py" . popweb-dict-youdao-input)
("C-c pd" . popweb-dict-dictcn-input)
("C-c pb" . popweb-dict-bing-input)
("C-c pu" . popweb-url-input)
:config
(use-package popweb-url)
;;(use-package color-picker)
(use-package popweb-dict))
(add-hook 'latex-mode-hook #'(lambda () (require 'popweb) (require 'popweb-latex) (popweb-latex-mode))))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/popweb/packages.el
(package! popweb
:recipe (:host github
:repo "manateelazycat/popweb"
:files ("*")))
设置 emacs-rime 输入法, Rime 在 ~/.local/share/emacs-rime
文件夹中设置。
;;; modules/baosize/chinese/config.el -*- lexical-binding: t; -*-
使用 ace-pinyin
以支持 avy
跳转到中文
使用 pinyin-lib
以支持 consult
搜索中文
(use-package ace-pinyin
:after avy
:init (setq ace-pinyin-use-avy t)
:config (ace-pinyin-global-mode t))
(use-package pinyinlib
:commands (pinyinlib-build-regexp-string)
:init
(defun orderless-regexp-pinyin (str)
(setf (car str) (pinyinlib-build-regexp-string (car str)))
str)
(advice-add 'orderless-regexp :filter-args #'orderless-regexp-pinyin))
禁用系统输入法
Fcitx5
什么的在 Emacs
上的体验很不好。
; disable gtk im modules for emacs-pgtk, add "Emacs*UseXIM: false" to ~/.Xresources to disable xim
(when (boundp 'pgtk-use-im-context-on-new-connection)
(setq pgtk-use-im-context-on-new-connection nil))
use-package
声明
(use-package rime
键绑定
:bind
(:map rime-mode-map ("C-`" . #'rime-send-keybinding))
自动切换输入法
:custom
(rime-disable-predicates
'(rime-predicate-evil-mode-p
meow-normal-mode-p
meow-keypad-mode-p
meow-motion-mode-p
rime-predicate-hydra-p
rime-predicate-prog-in-code-p
rime-predicate-space-after-cc-p
;; rime-predicate-org-in-src-block-p
rime-predicate-org-latex-mode-p
rime-predicate-punctuation-after-space-cc-p
rime-predicate-punctuation-after-ascii-p
rime-predicate-punctuation-line-begin-p
;; rime-predicate-space-after-ascii-p
;; rime-predicate-space-after-cc-p
rime-predicate-after-ascii-char-p))
Rime 本身相关设置
;; (rime-share-data-dir
;; (cl-some (lambda (dir)
;; (let ((abs-dir (expand-file-name dir)))
;; (when (file-directory-p abs-dir)
;; abs-dir)))
;; '("/usr/share/rime-data"
;; "/usr/share/local"
;; "/usr/share")))
(rime-user-data-dir (expand-file-name "~/.local/share/emacs-rime"))
(rime-show-candidate 'posframe)
(rime-posframe-style 'vertical)
设置默认输入法
(default-input-method "rime")
Org-mode
自启动
:config
(add-hook 'org-mode-hook (lambda () (activate-input-method default-input-method)))
防止堵塞、冻结 Emacs
(add-hook 'kill-emacs-hook (lambda () (ignore-errors (rime-lib-finalize)))))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/chinese/packages.el
(package! rime)
(package! ace-pinyin)
(package! pinyinlib)
(when (modulep! :editor evil) (package! evil-pinyin))
用 corfu 输入英文单词
;;; modules/baosize/corfu-english-helper/config.el -*- lexical-binding: t; -*-
(use-package corfu-english-helper
:defer t
:hook (text-mode . toggle-corfu-english-helper))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/corfu-english-helper/packages.el
(package! corfu-english-helper :recipe(:host github :repo "manateelazycat/corfu-english-helper" :files ("*")))
试着整理一下,当学习 Emacs Lisp
了。
定义基本变量,以及文件头
;;; modules/baosize/treesit-context/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defgroup treesit-context nil
"Show the context of the currently visible buffer contents."
:group 'treesit)
(defvar treesit-context--buffer (generate-new-buffer "*treesit-context-posframe-buffer*")
"Buffer used to display the context.")
(defvar treesit-context--list nil
"List used to store the context needs showing.")
(defvar treesit-context--timer nil
"Timer for updating the context.")
声明显示函数( Emacs Lisp
加载的时候会先读取所有源码,然后在源码中查找定义,也就是说无需计较声明顺序,不像某些 C 语言)
;;;###autoload
(defun treesit-context ()
"Show code context."
(interactive)
(unless (not (treesit-available-p))
(local-unset-key (kbd "C-g"))
(local-set-key (kbd "C-g") 'treesit-context-abort)
;; (add-hook 'post-command-hook #'treesit-context--update nil 'local)
(setq treesit-context--timer (run-with-idle-timer 0.1 t 'treesit-context--update))
(treesit-context--update)))
声明删除窗口的函数(不知道该怎么称呼这东西了)
;;;###autoload
(defun treesit-context-abort ()
"Abort showing code context."
(interactive)
(posframe-hide treesit-context--buffer)
(local-set-key (kbd "C-g") 'keyboard-quit)
;; (kill-buffer treesit-context--buffer)
;; (remove-hook 'post-command-hook #'treesit-context--update 'local)
(when treesit-context--timer
(cancel-timer treesit-context--timer)))
我们可以显示像 loop,fucntion,condition,class 之类的各种 tree-sitter node
,这个时候我们就要维护一个变量记录我们要显示什么东西了。
;;;###autoload
(defun treesit-context--add-to-list (node)
"Add the text of the node into `treesit-context--list'."
(if (or (string= (treesit-node-type node) "struct_specifier")
(string= (treesit-node-type node) "function_definition"))
(progn
(let* ((text (treesit-node-text node))
(buf (generate-new-buffer "*treesit-context-temp-buffer*"))
(text-showed nil))
(with-current-buffer buf
(goto-char (point-min)) (insert text)
(goto-char (point-min))
(setq text-showed (buffer-substring
(point-min) (line-end-position))))
(push text-showed treesit-context--list)
(kill-buffer buf)))))
我们需要从 tree-sitter
中查询上下文以显示代码层级。
;;;###autoload
(defun treesit-context--get-context-from-list ()
"Get the context of `treesit-context--list'"
(let ((context ""))
(dolist (text treesit-context--list)
(setq context (concat context text "\n")))
context))
光标是会动的,层级是会变化的,窗口是要更新的。
;;;###autoload
(defun treesit-context--update ()
"Update `treesit-context--ov'."
(unless (or (minibufferp) (not (buffer-live-p treesit-context--buffer)))
(setq treesit-context--list nil)
(ignore-errors
(let* ((node (treesit-node-at (point))))
(cl-loop while node
do (treesit-context--add-to-list node)
do (setq node (treesit-node-parent node)))))
(if treesit-context--list
(progn
(with-current-buffer treesit-context--buffer
(erase-buffer)
(insert (treesit-context--get-context-from-list)))
(when (posframe-workable-p)
(posframe-show treesit-context--buffer
:poshandler #'posframe-poshandler-window-top-right-corner
:background-color "#cde0ed"
:border-width 5
:border-color "#cde0ed")))
(posframe-hide treesit-context--buffer))))
vterm
模块
;;; modules/baosize/vterm-run/autoload.el -*- lexical-binding: t; -*-
;;;###if (modulep! :term vterm)
将内容发送给 vterm
;;;###autoload
(defun run-in-vterm (command)
"Execute string COMMAND in a new vterm.
Interactively, prompt for COMMAND with the current buffer's file
name supplied. When called from Dired, supply the name of the
file at point.
Like `async-shell-command`, but run in a vterm for full terminal features.
The new vterm buffer is named in the form `*foo bar.baz*`, the
command and its arguments in earmuffs.
When the command terminates, the shell remains open, but when the
shell exits, the buffer is killed."
(interactive
(list
(let* ((f (cond (buffer-file-name)
((eq major-mode 'dired-mode)
(dired-get-filename nil t))))
(filename (if f
(concat " " (shell-quote-argument f))
"")))
(read-shell-command "Terminal command: "
(cons filename 0)
(cons 'shell-command-history 1)
(list filename)))))
(+vterm/toggle nil)
(vterm-send-string command)
(vterm-send-return))
运行代码
;;;###autoload
(defun run-code ()
"运行代码"
(interactive)
(if buffer-file-name
(let ((file-name (shell-quote-argument
(file-name-sans-extension
(file-name-nondirectory buffer-file-name))))
(file-path (shell-quote-argument buffer-file-name))
(dir (shell-quote-argument
(if (doom-project-root) (doom-project-root)
(file-name-directory buffer-file-name)))))
(pcase major-mode
('c-ts-mode (run-in-vterm (concat "cd " dir " && "
"gcc -O2 -std=c11 -g3 "
file-path
" -o /tmp/c-" file-name
" && /tmp/c-" file-name)))
('c++-ts-mode (run-in-vterm (concat "cd " dir " && "
"g++ -O2 -std=gnu++17 -g3 "
file-path
" -o /tmp/cpp-" file-name
" && /tmp/cpp-" file-name)))
('python-ts-mode (run-in-vterm (concat "cd " dir " && "
"python " file-path)))
(_ (message "not supported"))))
(message "buffer-file-name is nil")))
进行代码错误检查
;;;###autoload
(defun run-cpp-fsanitize ()
"检查未定义行为"
(interactive)
(if buffer-file-name
(let ((filename (file-name-sans-extension (file-name-nondirectory buffer-file-name)))
(dir (if (doom-project-root) (doom-project-root) (file-name-directory buffer-file-name))))
(run-in-vterm (concat "cd " dir " && " "clang++ -O2 -std=c++17 -fsanitize=undefined " buffer-file-name " -o /tmp/cpp-" filename " && /tmp/cpp-" filename)))
(message "buffer-file-name is nil")))
绑定一些快捷键
;;; modules/baosize/vterm-run/config.el -*- lexical-binding: t; -*-
(when (modulep! :term vterm)
(bind-keys ("C-c o TAB" . run-in-vterm)
("C-c oo" . run-code)
("C-c ot" . +vterm/toggle)
("C-c oF" . run-cpp-fsanitize)))
切换窗口时更高效的选择( Alt + 数字键)
;;; modules/baosize/winum/config.el -*- lexical-binding: t; -*-
(use-package ace-window
:custom-face
(aw-leading-char-face ((t (:inherit font-lock-keyword-face :foreground unspecified :bold t :height 3.0))))
(aw-minibuffer-leading-char-face ((t (:inherit font-lock-keyword-face :bold t :height 1.0))))
(aw-mode-line-face ((t (:inherit mode-line-emphasis :bold t))))
:bind (([remap other-window] . ace-window))
:hook (emacs-startup . ace-window-display-mode)
:config
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))
(user-error "`toggle-window-split' only supports two windows")))
;; Select widnow via `M-1'...`M-9'
(defun aw--select-window (number)
"Slecet the specified window."
(when (numberp number)
(let ((found nil))
(dolist (win (aw-window-list))
(when (and (window-live-p win)
(eq number
(string-to-number
(window-parameter win 'ace-window-path))))
(setq found t)
(aw-switch-to-window win)))
(unless found
(message "No specified window: %d" number)))))
(dotimes (n 9)
(bind-key (format "M-%d" (1+ n))
(lambda ()
(interactive)
(aw--select-window (1+ n))))))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/winum/packages.el
;; (package! winum)
Emacs 杀器之一,高亮多个匹配单词并提供重命名、跳转等多种功能
;;; modules/baosize/symbol-overlay/config.el -*- lexical-binding: t; -*-
(use-package symbol-overlay
:defer t
:hook
(prog-mode . symbol-overlay-mode)
(text-mode . symbol-overlay-mode)
:bind
(("M-i" . symbol-overlay-put)
("M-n" . symbol-overlay-switch-forward)
("M-p" . symbol-overlay-switch-backward)
("<f7>" . symbol-overlay-mode)
("<f8>" . symbol-overlay-remove-all)))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/symbol-overlay/packages.el
(package! symbol-overlay)
自动保存
;;; modules/baosize/super-save/config.el -*- lexical-binding: t; -*-
(use-package super-save
:defer 0.5
:config
(add-to-list 'super-save-triggers 'switch-window)
(setq super-save-exclude '(".gpg"))
(setq super-save-idle-duration 1)
(setq super-save-all-buffers 1)
(setq super-save-auto-save-when-idle t)
(setq super-save-silent t)
(super-save-mode 1))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/super-save/packages.el
(package! super-save)
记录按键频率,方便优化
;;; modules/baosize/keyfreq/config.el -*- lexical-binding: t; -*-
(use-package keyfreq
:defer t
:hook (doom-first-input . keyfreq-mode)
(doom-first-input . keyfreq-autosave-mode))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/keyfreq/packages.el
(package! keyfreq
:recipe (:host github :repo "dacap/keyfreq"))
在文件中将颜色标上对应高亮(比如”SpringGreen3”在 el
文件中会变绿)
;;; modules/baosize/colorful-mode/config.el -*- lexical-binding: t; -*-
(use-package colorful-mode
:defer t
:hook (prog-mode text-mode))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/super-save/packages.el
(package! colorful-mode)
为 lsp-mode 和 eglot 进行加速(将 json 转换为 elisp 形式,加速解析)
在 emacs 30 之后, emacs 对 json 的解析速度已经超越了对于 emacs-lisp array 的解析速度:)到那时 emacs-lsp-booster 如果再 parse json 造成的将是负优化。
没啥好说的,都是从项目主页抄的。
;;; modules/baosize/emacs-lsp-booster/config.el
(if (modulep! :tools lsp +eglot)
(when (<= emacs-major-version 29)
(use-package eglot-booster
:after eglot
:config (eglot-booster-mode)))
(progn (defun lsp-booster--advice-json-parse (old-fn &rest args)
"Try to parse bytecode instead of json."
(or
(when (equal (following-char) ?#)
(let ((bytecode (read (current-buffer))))
(when (byte-code-function-p bytecode)
(funcall bytecode))))
(apply old-fn args)))
(advice-add (if (progn (require 'json)
(fboundp 'json-parse-buffer))
'json-parse-buffer
'json-read)
:around
#'lsp-booster--advice-json-parse)
(defun lsp-booster--advice-final-command (old-fn cmd &optional test?)
"Prepend emacs-lsp-booster command to lsp CMD."
(let ((orig-result (funcall old-fn cmd test?)))
(if (and (not test?) ;; for check lsp-server-present?
(not (file-remote-p default-directory)) ;; see lsp-resolve-final-command, it would add extra shell wrapper
lsp-use-plists
(not (functionp 'json-rpc-connection)) ;; native json-rpc
(executable-find "emacs-lsp-booster"))
(progn
(when-let ((command-from-exec-path (executable-find (car orig-result)))) ;; resolve command from exec-path (in case not found in $PATH)
(setcar orig-result command-from-exec-path))
(message "Using emacs-lsp-booster for %s!" orig-result)
(cons "emacs-lsp-booster" orig-result))
orig-result)))
(advice-add 'lsp-resolve-final-command :around #'lsp-booster--advice-final-command)))
;; -*- no-byte-compile: t; -*-
;;; modules/baosize/emacs-lsp-booster/packages.el
(when (and (modulep! :tools lsp +eglot) (<= emacs-major-version 29))
(package! eglot-booster
:recipe(:host github
:repo "jdtsmith/eglot-booster")))
要是没有 emacs-lsp-booster 可执行文件的话……
;;; modules/baosize/emacs-lsp-booster/doctor.el
(unless (or (executable-find "emacs-lsp-booster") (>= emacs-major-version 30))
(warn! "LSP will crash when emacs-lsp-booster can't found in PATH!"))
在 modeline 上的彩虹猫
;;; modules/baosize/nyan/config.el -*- lexical-binding: t; -*-
(use-package nyan-mode
:hook (doom-modeline-mode)
:custom
;; (nyan-animate-nyancat t)
(nyan-wavy-trail t))
;; -*- no-byte-compile: t -*-
;;; modules/baosize/nyan/packages.el
(package! nyan-mode
:recipe (:host github
:repo "zakudriver/nyan-mode"
:files ("*")))
类似于 neovim 中 flash.nvim 的远程跳转编辑( Operator-PENDING mode
下的 r 键)
;;; modules/baosize/avy-thing-edit/config.el -*- lexical-binding: t; -*-
(use-package thing-edit
:defer t
:config
(use-package avy-thing-edit
:defer t
:custom
(avy-thing-edit-jump-command 'avy-goto-word-0)))
;; -*- no-byte-compile: t -*-
;;; modules/baosize/avy-thing-edit/packages.el
(package! avy-thing-edit
:recipe (:host github
:repo "lyjdwh/avy-thing-edit"))
(package! thing-edit
:recipe (:host github
:repo "manateelazycat/thing-edit"))
我尝试用这个模块以将 eglot 打造得和 vscode 一样优秀( lsp-mode 提供了一个基于 lsp 的高亮,这对于 eglot 来说是极为困难的)
它目前包含了 breadcrumb 和 eldoc-box 。
;;; modules/baosize/super-eglot/config.el -*- lexical-binding: t; -*-
面包屑导航
(use-package breadcrumb
:defer t
:hook (prog-mode . breadcrumb-local-mode))
弹出文档和提示
(use-package eldoc-box
:custom
(eldoc-box-lighter nil)
(eldoc-box-only-multi-line t)
(eldoc-box-clear-with-C-g t)
:custom-face
(eldoc-box-border ((t (:inherit posframe-border :background unspecified))))
(eldoc-box-body ((t (:inherit tooltip))))
:hook (eglot-managed-mode . eldoc-box-hover-at-point-mode)
:config
;; Prettify `eldoc-box' frame
(setf (alist-get 'left-fringe eldoc-box-frame-parameters) 8
(alist-get 'right-fringe eldoc-box-frame-parameters) 8))
code-action 提示
(use-package sideline
:defer t
:hook (eglot-managed-mode . sideline-mode)
:init
(setq sideline-backends-left-skip-current-line t ; don't display on current line (left)
sideline-backends-right-skip-current-line t ; don't display on current line (right)
sideline-order-left 'down ; or 'up
sideline-order-right 'up ; or 'down
sideline-format-left "%s " ; format for left aligment
sideline-format-right " %s" ; format for right aligment
sideline-priority 100 ; overlays' priority
sideline-display-backend-name t)) ; display the backend name
(use-package sideline-eglot
:defer 1
:init
(setq sideline-backends-right '(sideline-eglot)))
;; -*- no-byte-compile: t -*-
;;; modules/baosize/super-eglot/packages.el
(package! breadcrumb)
(package! eldoc-box)
(package! sideline)
(package! sideline-eglot
:recipe (:host github
:repo "emacs-sideline/sideline-eglot"))
不依赖于 lsp-mode 的 DAP 服务
;;; modules/baosize/dape/config.el -*- lexical-binding:t; -*-
(use-package dape
:bind (("<f5>" . dape))
:custom (dape-buffer-window-arrangment 'right)
:config
;; Save buffers on startup, useful for interpreted languages
(add-hook 'dape-on-start-hooks
(defun dape--save-on-start ()
(save-some-buffers t t))))
;; -*- no-byte-compile: t -*-
;;; modules/baosize/dape/packages.el
(package! dape)