emacs-china / Spacemacs-rocks

Happy Hacking Emacs & Spacemacs (Simplified Chinese)
http://book.emacs-china.org
2.12k stars 323 forks source link
emacs learn-emacs spacemacs

-- mode: org; --

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD: // www.pirilamp.org/styles/lib/js/jquery.stickytableheaders.js 404 Now

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD:

+HTML_HEAD:

+AUTHOR: zilongshanren

+CREATOR: li-xinyang

+TITLE: Master Emacs in 21 Days

+EMAIL: guanghui8827@gmail.com

+OPTIONS: toc:3 num:nil

+STARTUP: showall

+OPTIONS: ^:nil

GitHub [[https://github.com/emacs-china/Spacemacs-rocks][Emacs Rocks]] 作者 [[https://github.com/zilongshanren/][zilongshanren]] 编者 [[https://github.com/li-xinyang][li-xinyang]], [[https://github.com/lsytj0413][lsytj0413]]

在这一季中我们计划用 21 天学习 Emacs 的使用。从基础安装开始到可以 运用到工作生产环节中。为了确保学习质量,请务必完成和理解计划中的每一项学习任务。(新的系列全部使用 Vallina Emacs)

Update(2022 年 1 月 15 日): 本系列视频教程最早录制于 2016 年,距今已经有 5 年多的历史了, 在此期间 Emacs 社区又进化了非常多。出现了更活跃的 Doom Emacs 社区配置,拥有 GCC Emacs,所以 现在回头来看当时的视频教程,显得多少有些过时了。以前 21 天的视频教程显得有些冗长,我打算在 2022 年 更新所有这些视频教程,并且控制每一个视频的时长(20 分钟左右),希望这个系列教程能够帮助到大家。

本视频的代码仓库: https://gitee.com/emacs-china/emacs.d

原来 21 天的视频地址:

原来 21 天的教程地址:

[[http://book1.emacs-china.org/][《21天学会Emacs》]]

视频地址如下:

说在最前面

如果你第一次听说 Emacs 你可以在[[http://emacs.sexy/][性感的 Emacs(Emacs is Sexy)]]一文中找到使用它的理 由!我相信你一定会被它的强大所吸引。

如果你还没有安装 Emacs 可以在 Doom Emacs 的安装文档中查找[[https://github.com/hlissner/doom-emacs/blob/master/docs/getting_started.org#install][安装方法]],根据你所使用的操作系统去下载对应 版本的 Emacs 就行了。(新手不推荐从源码进行编译安装,因为这会比较困难)

因为考虑到比较多的用户之前可能没有接触过 Mac 或者 Linux,所以新的系列视频我会直接使用 Windows 系统来 进行演示。Windows 下配置 Emacs 环境可以参考我之前在论坛写的[[https://emacs-china.org/t/windows-emacs/7907][文章]]。

由于 Windows 系统的一些限制,某一些 Emacs 的功能(Magit,lsp 等)表现并不是很好(主要指的是性能),读者可以 尝试安装 WLS 来使用 Emacs,具体方法可以参考[[https://hkvim.com/post/windows-setup/][这篇文章]]。

在开始本教程之前请务必先完成 Emacs 提供的官方教程(完成时间大约 30 分钟),它可 以通过使用 =C-h t= (同时按住 Ctrl 与 h 键,接着按 t 键 =t= 在这里代表 tutorial) 在 Emacs 中直接将其打开。(当然你也可以在观看视频之后再去阅读 Emacs Tutorial)

Emacs Lisp 是 Emacs 所用的编程语言,你可以在阅读[[https://learnxinyminutes.com/docs/elisp/][这篇教程(Learn X in Y Minutes)]] 后很快地了解它的基础用法。

** 基础操作 在讲解基本操作之前,先跟大家讲一下改键。改键的作用除了可以让你的小拇指更健康以外,还可以统一我们的交流语言, 让大家后面学习过程可以更轻松。(这部分如果没有相关基础,强烈建议先看视频是怎么操作的)

Windows 键盘布局:

[[./Images/1600px-KB_United_States-NoAltGr.svg.png]]

Windows 改键方法:

使用 SharpKeys 来把 Left Window 键改成 Left Alt 键,Left Alt 键改成 App 键,大小写键改成 Ctrl 键。

[[./Images/sharpKeys.png]]

Emacs 默认 Alt 键就是 Meta,而 App 键我们改成 super 键,可以通过下面的 Elisp 代码来完成。

+begin_src emacs-lisp

(setq w32-apps-modifier 'super) ; 通过SharpKeys改成了 Application

+end_src

Mac 键盘布局:

[[./Images/mac-keyboard-layout.jpeg]]

Mac 下面大小写键改成 Ctrl 键(参考视频),Mac 系列只需要修改这个按键,其他的按键可以通过 Elisp 来修改:

+begin_src emacs-lisp

(setq mac-option-modifier 'meta mac-command-modifier 'super)

+end_src

常见符号所代表的意义如下

光标的移动是编辑器中最常用的操作所以必须熟知。(只需要掌握这些基础的光标移动操作,后续我会介绍 Vim 的光标移动操作, 我个人认为 Vim 的光标移动更高效,大家先熟知这几个常用的移动命令即可。)

为什么不使用鼠标而去学习这些奇怪的按键组合呢?因为这些按键在某些时候是非常高效的,比如在 Emacs 里面,Shell 终端里面, 甚至整个 Mac 系统都内置了这种按键。我本人在使用 Vim 或者其他 IDE 的时候也会使用 Emacs 的这套按键。

学习可以迁移的技能,这一点非常重要!一旦学会了将可以终身受用。

现在我们可以把 Mac 下面的复制、粘贴、剪切、全选等命令移植到 Emacs 中来了,并且这一套按键是跨平台的。

+begin_src emacs-lisp

(global-set-key (kbd "s-a") 'mark-whole-buffer) ;;对应Windows上面的Ctrl-a 全选 (global-set-key (kbd "s-c") 'kill-ring-save) ;;对应Windows上面的Ctrl-c 复制 (global-set-key (kbd "s-s") 'save-buffer) ;; 对应Windows上面的Ctrl-s 保存 (global-set-key (kbd "s-v") 'yank) ;对应Windows上面的Ctrl-v 粘贴 (global-set-key (kbd "s-z") 'undo) ;对应Windows上面的Ctrol-z 撤销 (global-set-key (kbd "s-x") 'kill-region) ;对应Windows上面的Ctrol-x 剪切

+end_src

** 内置功能

Emacs 功能强大,但是部分功能默认情况下并未开启。下面就有几个例子,

编辑器内显示行号可使用 =M-x linum-mode= 来开启。

获取帮助

Emacs 是一个富文档编辑器(Self document, extensible editor)而下面的三种方法在学 习 Emacs 的过程中也非常重要。他们分别是,

*** 给 Windows 右键菜单添加 OpenWithEmacs 功能

+begin_src

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT*\shell] [HKEY_CLASSES_ROOT*\shell\openwemacs] @="&Edit with Emacs" [HKEY_CLASSES_ROOT*\shell\openwemacs\command] @="C:\emax64\bin\emacsclientw.exe -n \"%1\"" [HKEY_CLASSES_ROOT\Directory\shell\openwemacs] @="Edit &with Emacs" [HKEY_CLASSES_ROOT\Directory\shell\openwemacs\command] @="C:\emax64\bin\emacsclientw.exe -n \"%1\""

+end_src

使用这个 OpenWithEmacs 的功能,Emacs 需要开启 Server Mode,代码如下:

+begin_src emacs-lisp

(server-mode 1)

+end_src

视频地址如下:

** 学习基础 Elisp

请务必完成[[https://learnxinyminutes.com/docs/elisp/][这篇教程(Learn X in Y Minutes)]]来了解 Elisp 的使用(阅读时间大约 15 分钟),你也可以在[[https://learnxinyminutes.com/docs/zh-cn/elisp-cn/][这里]]找到它的中文版。Emacs Lisp 为一个函数式的语言,所以它全部 功能都是由函数来实现的。

下面为一些简单的例子

+BEGIN_SRC emacs-lisp

;; 2 + 2 (+ 2 2)

;; 2 + 3 4 (+ 2 ( 3 4))

;; 定义变量 (setq name "username") (message name) ; -> "username"

;; 定义函数 (defun func () (message "Hello, %s" name))

;; 执行函数 (func) ; -> Hello, username

;; 设置快捷键 (global-set-key (kbd "") 'func)

;; 使函数可直接被调用可添加 (interactive) (defun func () (interactive) (message "Hello, %s" name))

+END_SRC

** 开始 Hacking!

Emacs 的配置文件默认保存在 =~/.emacs.d/init.el= 文件中。(如果其不存在可自行创建, 配置文件也可保存在 =~/.emacs= 文件中,他们之间的区别我们会在后面做讨论)

注意: 如果希望把配置放在 =~/.emacs.d/init.el= 文件中,那么需要手工删除 =~/.emacs= 文件。

在开始配置之前让我们先来区别 Emacs 中 Major Mode 与 Minor Mode 的区别。Major Mode 通常是定义对于一种文件类型编辑的核心规则,例如语法高亮、缩进、快捷键绑定等。 而 Minor Mode 是除去 Major Mode 所提供的核心功能以外的额外编辑功能(辅助功能)。 例如在下面的配置文件中 =tool-bar-mode= 与 =linum-mode= 等均为 Minor Mode*。

简单来说就是,一种文件类型同时只能存在一种 Major Mode 但是它可以同时激活一种或多 种 Minor Mode。如果你希望知道当前的模式信息,可以使用 =C-h m= 来显示当前所有开启 的全部 Minor Mode 的信息。

简单的编辑器自定义

下面是一些简单的编辑器配置信息,你需要做的就是将其写入你的配置文件中 ( =~/.emacs.d/init.el= )即可。

+BEGIN_SRC emacs-lisp

;; 关闭工具栏,tool-bar-mode 即为一个 Minor Mode (tool-bar-mode -1)

;; 关闭文件滑动控件 (scroll-bar-mode -1)

;; 显示行号 (global-linum-mode 1)

;; 更改光标的样式(不能生效,解决方案见第二集) (setq cursor-type 'bar)

(icomplete-mode 1)

;; 快速打开配置文件 (defun open-init-file() (interactive) (find-file "~/.emacs.d/init.el"))

;; 这一行代码,将函数 open-init-file 绑定到 键上 (global-set-key (kbd "") 'open-init-file)

+END_SRC

在每次编辑配置文件后,刚刚做的修改并不会立刻生效。这时你需要重启编辑器或者重新加 载配置文件。重新加载配置文件你需要在当前配置文件中使用 =M-x load-file= 双击两次 回车确认默认文件名,或者使用 =M-x eval-buffer= 去执行当前缓冲区的所有 Lisp 命令。 你也可以使用 =C-x C-e= 来执行某一行的 Lisp 代码。这些可使刚刚修改的配置文件生效。 当然你也可以将这些函数绑定为快捷键。

** 插件管理

使用默认的插件管理系统(可在菜单栏 =Options > Manage Emacs Packages= 中找到)安 装 [[http://company-mode.github.io/][Company]] 插件,他是一个用于代码补全的插件。它的名字代表补全一切的意思( Comp lete Any thing)。因为默认的插件管理系统提供的插件十分有限,所以我们会在之后的 几天中继续将其强化。

使用的下面的配置将 Company-mode 在全局模式下激活

+BEGIN_SRC emacs-lisp

                                      ; 开启全局 Company 补全

(global-company-mode 1)

;; company mode 默认选择上一条和下一条候选项命令 M-n M-p (define-key company-active-map (kbd "C-n") 'company-select-next) (define-key company-active-map (kbd "C-p") 'company-select-previous)

+END_SRC

** Org-mode

简单的 Org-mode 使用,它可以列出提纲,并方便地使用 =tab= 键来对其进行展开与关闭。 =C-c C-t= 可以将一个条目转换成一条待办事件。

+BEGIN_SRC org

, 为一级标题 , 为二级标题 , 为三级标题并以此类推

+END_SRC

视频地址如下:

** 关于 lexical binding

+begin_src emacs-lisp

;;在文件最开头添加地个 文件作用域的变量设置,设置变量的绑定方式 ;; -- lexical-binding: t -- (let ((x 1)) ; x is lexically bound. (+ x 3)) ⇒ 4

(defun getx () x) ; x is used free in this function.

(let ((x 1)) ; x is lexically bound. (getx)) ;;error→ Symbol's value as variable is void: x

+end_src

关于[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html][lexical binding]] 更多的细节,可以自行阅读 Emacs 的官方文档。

+begin_src emacs-lisp

;; 更改显示字体大小 16pt ;; http://stackoverflow.com/questions/294664/how-to-set-the-font-size-in-emacs (set-face-attribute 'default nil :height 160);;

;;让鼠标滚动更好用 (setq mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control) . nil))) (setq mouse-wheel-progressive-speed nil)

+end_src

** 配置 gnu 和 melpa 镜像

在进行美化之前我们需要配置插件的源(默认的源非常有限),最常使用的是 [[https://melpa.org/][MELPA]] (Milkypostman's Emacs Lisp Package Archive)。它有非常多的插件(3000 多个插件)。 添加源后,我们就可以使用 =M-x package-list-packages= 来查看所有 MELPA 上的插件了。在表单中可以使用 =i= 来标记安装 =d= 来标记删除, =U= 来更新,并用 =x= 来确认。你也可以使用 =u= 来撤销标记操作。

你可以直接将下面的代码复制到你的配置文件顶端,从而直接使用 Melpa 作为插件的源。

+BEGIN_SRC emacs-lisp

(require 'package) (setq package-archives '(("gnu" . "http://elpa.zilongshanren.com/gnu/")

                       ("melpa" . "http://elpa.zilongshanren.com/melpa/")))

(package-initialize)

;;防止反复调用 package-refresh-contents 会影响加载速度 (when (not package-archive-contents) (package-refresh-contents))

;;modeline上显示我的所有的按键和执行的命令 (package-install 'keycast) (keycast-mode t)

+END_SRC

** 增强 minibuffer 补全:[[https://github.com/minad/vertico][vertico]] 和 [[https://github.com/oantolin/orderless][Orderless]]

+begin_src emacs-lisp

(package-install 'vertico) (vertico-mode t)

(package-install 'orderless) (setq completion-styles '(orderless))

+end_src

** 配置 [[https://github.com/minad/marginalia][Marginalia]] 增强 minubuffer 的 annotation

+begin_src emacs-lisp

(package-install 'marginalia) (marginalia-mode t)

+end_src

** minibuffer action 和自适应的 context menu:[[https://github.com/oantolin/embark][Embark]]

+begin_src emacs-lisp

(package-install 'embark) (global-set-key (kbd "C-;") 'embark-act) (setq prefix-help-command 'embark-prefix-help-command)

+end_src

** 增强文件内搜索和跳转函数定义:[[https://github.com/minad/consult][Consult]]

+begin_src emacs-lisp

(package-install 'consult) ;;replace swiper (global-set-key (kbd "C-s") 'consult-line) ;;consult-imenu

+end_src

视频地址如下:

** 手工安装插件和更多 Emacs 内置功能定制

如果你想深入学习 Emacs Lisp 可以阅读 GNU 提供的 [[https://www.gnu.org/software/emacs/manual/html_mono/eintr.html][An Introduction to Programming in Emacs Lisp]] 。(也可以 =M-x info= 然后选择 Emacs Lisp Intro)

我们先解决前一天中遇到的一些问题。首先是在对象是一个缓冲区局部变量(Buffer-local variable)的时候,比如这里的 =cursor-type= ,我们需要区分 =setq= 与 =setq-default= : =setq= 设置当前缓冲区(Buffer)中的变量值, =setq-default= 设 置的为全局的变量的值(具体内容可以在 [[http://stackoverflow.com/questions/18172728/the-difference-between-setq-and-setq-default-in-emacs-lisp][StackOverflow 找到]])。下面是一个例子,用于 设置光标样式的方法。

+BEGIN_SRC emacs-lisp

(setq-default cursor-type 'bar) (show-paren-mode t)

;;另外一件安装插件的方法 (add-to-list 'load-path (expand-file-name "~/.emacs.d/awesome-tab/"))

(require 'awesome-tab)

(awesome-tab-mode t)

(defun awesome-tab-buffer-groups () "awesome-tab-buffer-groups' control buffers' group rules. Group awesome-tab with mode if buffer is derived fromeshell-mode' emacs-lisp-mode'dired-mode' org-mode'magit-mode'. All buffer name start with will group to \"Emacs\". Other buffer group by `awesome-tab-get-group-name' with project name." (list (cond ((or (string-equal "" (substring (buffer-name) 0 1)) (memq major-mode '(magit-process-mode magit-status-mode magit-diff-mode magit-log-mode magit-file-mode magit-blob-mode magit-blame-mode))) "Emacs") ((derived-mode-p 'eshell-mode) "EShell") ((derived-mode-p 'dired-mode) "Dired") ((memq major-mode '(org-mode org-agenda-mode diary-mode)) "OrgMode") ((derived-mode-p 'eaf-mode) "EAF") (t (awesome-tab-get-group-name (current-buffer))))))

+END_SRC

其次就是它使用到了 =quote=, 它其实就是我们之前常常见到的 ='= (单引号)的完全体。 因为它在 Lisp 中十分常用,所以就提供了简写的方法。

+BEGIN_SRC emacs-lisp

;; 下面两行的效果完全相同的 (quote foo) 'foo

+END_SRC

=quote= 的意思是不要执行后面的内容,返回它原本的内容(具体请参考下面的例子)

+BEGIN_SRC emacs-lisp

(print '(+ 1 1)) ;; -> (+ 1 1) (print (+ 1 1)) ;; -> 2

+END_SRC

更多关于 =quote= 的内容可以在[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Quoting.html][这里]]找到,或者在[[http://stackoverflow.com/questions/134887/when-to-use-quote-in-lisp][这里]]找到 StackOverflow 上对于它的讨论。

因为通常我们的配置文件以及项目文件均使用版本控制系统,所以自动生成的备份文件就显 得有些多余。我们还可以禁止 Emacs 自动生成备份文件,例如 =init.el~= 。( =~= 为后 缀的文件为自动生成的备份文件)我们可以使用下面的方法将其关闭。

+BEGIN_SRC emacs-lisp

(setq make-backup-files nil)

+END_SRC

使用下面的配置来加入最近打开过文件的选项让我们更快捷的在图形界面的菜单中打开最近 编辑过的文件。

+BEGIN_SRC emacs-lisp

(require 'recentf) (recentf-mode 1) (setq recentf-max-menu-item 10)

;; 这个快捷键绑定可以用之后的插件 counsel 代替 ;; (global-set-key (kbd "C-x C-r") 'recentf-open-files)

+END_SRC

=require= 的意思为从文件中加载特性,你可以在杀哥的网站读到关于 Emacs Lisp 库系统 的更多内容,文章在[[http://ergoemacs.org/emacs/elisp_library_system.html][这里]]。

使用下面的配置文件将删除功能配置成与其他图形界面的编辑器相同,即当你选中一段文字 之后输入一个字符会替换掉你选中部分的文字。

+BEGIN_SRC emacs-lisp

(delete-selection-mode 1)

+END_SRC

下面的这些函数可以让你找到不同函数,变量以及快捷键所定义的文件位置。因为非常常用 所以我们建议将其设置为与查找文档类似的快捷键(如下所示),

** 使用外网命令行工具 下载安装 [[https://github.com/m-parashar/emax64/releases/download/20200930/emax.7z][emax]],配置 emacs 加载路径

+begin_src emacs-lisp

(progn (defvar emax-root (concat (expand-file-name "~") "/emax")) (defvar emax-bin (concat emax-root "/bin")) (defvar emax-bin64 (concat emax-root "/bin64"))

(setq exec-path (cons emax-bin exec-path))
(setenv "PATH" (concat emax-bin ";" (getenv "PATH")))

(setq exec-path (cons emax-bin64 exec-path))
(setenv "PATH" (concat emax-bin64 ";" (getenv "PATH")))

(setq emacsd-bin (concat user-emacs-directory "bin"))
(setq exec-path (cons  emacsd-bin exec-path))
(setenv "PATH" (concat emacsd-bin  ";" (getenv "PATH")))

;;可选安装msys64
;;下载地址: http://repo.msys2.org/mingw/sources/
(setenv "PATH" (concat "C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;" (getenv "PATH")))

;; (dolist (dir '("~/emax/" "~/emax/bin/" "~/emax/bin64/" "~/emax/lisp/" "~/emax/elpa/"))
;;   (add-to-list 'load-path dir))
)

+end_src

使用 M-x shell 来学习命令行操作,可以参考 https://missing.csail.mit.edu/2020/ 来学习,living in Emacs。

** 使用 Emacs 来打开文件管理器

+begin_src emacs-lisp

(shell-command-to-string "explorer.exe C:\")

(shell-command-to-string "explorer.exe ~/.emacs.d")

(shell-command-to-string (encode-coding-string (replace-regexp-in-string "/" "\\" (format "explorer.exe %s" (expand-file-name "~/.emacs.d"))) 'gbk))

(defun consult-directory-externally (file) "Open FILE externally using the default application of the system." (interactive "fOpen externally: ") (if (and (eq system-type 'windows-nt) (fboundp 'w32-shell-execute)) (shell-command-to-string (encode-coding-string (replace-regexp-in-string "/" "\\" (format "explorer.exe %s" (file-name-directory (expand-file-name file)))) 'gbk)) (call-process (pcase system-type ('darwin "open") ('cygwin "cygstart") (_ "xdg-open")) nil 0 nil (file-name-directory (expand-file-name file)))))

(define-key embark-file-map (kbd "E") #'consult-directory-externally) ;;打开当前文件的目录 (defun my-open-current-directory () (interactive) (consult-directory-externally default-directory))

+end_src

** 增强 embark 和 consult,批量搜索替换大杀器

+BEGIN_SRC emacs-lisp

(package-install 'embark-consult) (package-install 'wgrep) (setq wgrep-auto-save-buffer t)

(eval-after-load 'consult '(eval-after-load 'embark '(progn (require 'embark-consult) (add-hook 'embark-collect-mode-hook

'consult-preview-at-point-mode))))

(define-key minibuffer-local-map (kbd "C-c C-e") 'embark-export-write)

;;使用ripgrep来进行搜索 ;;consult-ripgrep

;;everyting ;;consult-locate ;; 配置搜索中文 (progn (setq consult-locate-args (encode-coding-string "es.exe -i -p -r" 'gbk)) (add-to-list 'process-coding-system-alist '("es" gbk . gbk)) ) (eval-after-load 'consult (progn (setq consult-narrow-key "<" consult-line-numbers-widen t consult-async-min-input 2 consult-async-refresh-delay 0.15 consult-async-input-throttle 0.2 consult-async-input-debounce 0.1) ))

+END_SRC

** 使用拼音进行搜索

+begin_src emacs-lisp

(package-install 'pyim)

(defun eh-orderless-regexp (orig_func component) (let ((result (funcall orig_func component))) (pyim-cregexp-build result)))

(defun toggle-chinese-search () (interactive) (if (not (advice-member-p #'eh-orderless-regexp 'orderless-regexp)) (advice-add 'orderless-regexp :around #'eh-orderless-regexp) (advice-remove 'orderless-regexp #'eh-orderless-regexp)))

(defun disable-py-search (&optional args) (if (advice-member-p #'eh-orderless-regexp 'orderless-regexp) (advice-remove 'orderless-regexp #'eh-orderless-regexp)))

;; (advice-add 'exit-minibuffer :after #'disable-py-search) (add-hook 'minibuffer-exit-hook 'disable-py-search)

(global-set-key (kbd "s-p") 'toggle-chinese-search)

+end_src

** Emacs 也很美

高亮当前行,当文本内容很多时可以很容易找到光标的位置。

+BEGIN_SRC emacs-lisp

(global-hl-line-mode 1)

+END_SRC

安装主题

+BEGIN_SRC emacs-lisp

(package-install 'monokai-theme)

+END_SRC

然后使用下面的配置使其每次打开编辑器时加载主题,

+BEGIN_SRC emacs-lisp

(load-theme 'monokai 1)

+END_SRC

使用 =M-x customize-group= 后选择对应的插件名称,可以进入可视化选项区对指定的插 件做自定义设置。当选择 Save for future session 后,刚刚做的设计就会被保存在你的 配置文件( =init.el= )中。关于各个插件的安装与使用方法通常都可以在其官方页面找 到(GitHub Pages 或者是项目仓库中的 README 文件)。我们强烈建议大家在安装这些插 件后阅读使用方法来更好的将它们使用到你的日常工作当中使效率最大化。

视频地址如下:

** 使用多文件存储配置文件

将不同的配置代码放置到不同的文件中,使其模块化,这让我们的后续维护变得更加简单。 下面是我们现在的 =~/.emacs.d/= 目录中的样子,

+BEGIN_EXAMPLE

├── auto-save-list # 自动生成的保存数据 ├── elpa # 下载的插件目录 ├── init.el # 我们的配置文件 └── recentf # 最近访问的文件列表

+END_EXAMPLE

通常我们只保存配置文件和对其进行版本控制,其他的插件均为在第一次使用编辑器时再通 过网络重新下载,当然你也可以选择将全部配置文件进行版本控制来保证自己时刻拥有最稳 定的生产环境。

custom.el

+begin_src emacs-lisp

(setq custom-file (expand-file-name "~/.emacs.d/custom.el")) (load custom-file 'no-error 'no-message)

+end_src

现在我们想将原本混合在一起的配置文件分为下面的几个模块(每一个模块为一个独立的配 置文件并将其保存在指定的子目录中),它们分别是

+BEGIN_EXAMPLE

init-packages.el # 插件管理 init-ui.el # 视觉层配置 init-better-defaults.el # 增强内置功能 init-keybindings.el # 快捷键绑定 init-org.el # Org 模式相关的全部设定 custom.el # 存放使用编辑器接口产生的配置信息

+END_EXAMPLE

下面为将配置文件进行模块化后的目录结构,

+BEGIN_EXAMPLE

├── init.el └── lisp ├── custom.el ├── init-better-defaults.el ├── init-keybindings.el ├── init-packages.el ├── init-ui.el └── init-org.el

+END_EXAMPLE

使用模块化配置就可以让我们在之后的配置中迅速的定位与更改配置内容,让整个过程变得 更有条理也更加高效。

和之前一样 =init.el= 是配置文件的入口,现在它便成为了所有模块配置文件的入口,所 以要使用这些模块时,我们需要在其中引用需要加载的模块。下面以 =init-packages.el= (此配置为添加插件的模块) 为例,详细说明如何模块化以及应用的方法。

下面为 =~/.emacs.d/lisp/init-packages.el= 模块中的代码

+BEGIN_SRC emacs-lisp

(require 'package) (setq package-archives '(("gnu" . "http://elpa.zilongshanren.com/gnu/")

                       ("melpa" . "http://elpa.zilongshanren.com/melpa/")))

(package-initialize)

;;防止反复调用 package-refresh-contents 会影响加载速度 (when (not package-archive-contents) (package-refresh-contents))

;; 文件末尾 (provide 'init-packages)

+END_SRC

下面为 =~/.emacs.d/init.el= 入口文件中的代码

+BEGIN_SRC emacs-lisp

(add-to-list 'load-path "~/.emacs.d/lisp/")

;; Package Management ;; ----------------------------------------------------------------- (require 'init-packages)

+END_SRC

模块化要做的其实非常简单,我们要做的其实就是把某一个更改编辑器某定部分(例如,插 件管理,显示层,快捷键绑定等)的配置代码写入一个独立的文件中并在末尾为其添加 =(provide 'module-name)= (这里我们的模块名为 =init-packages= )使其可以在入口文件 中被调用,然后再在入口文件中将其引用既可。

这里需要注意的是,我们需要在入口文件中添加 =(add-to-list 'load-path "~/.emacs.d/lisp/")= 这可以让 Emacs 找到需要加载的模块所处的位置。

这里推荐大家两个还不错的 Emacs 配置:

https://github.com/condy0919/.emacs.d

https://github.com/seagle0128/.emacs.d

** 使用 Org 来管理配置文件 Org-mode 下的文学编程将颠覆你对于 Emacs 的看法。因为我们也可以使用 Org 来管理 Emacs 的配置文件(本人其实更倾向于用多个文件来管理配置文件)。

在 Org-mode 中你可以直接开启新的缓冲区(Buffer)直接用相应的 Major Mode 来编辑代码块内的内容。在代码块中使用 C-c ' 会直接打开对应模式的缓冲区(不仅限于 Lisp)。 这样就使在 Org-mode 中编辑代码变的十分方便快捷。

使用 <s 然后 Tab 可以直接插入代码块的代码片段(Snippet),更多类似的代码片段 (Org-mode Easy Templates)可以在这里找到。

+BEGIN_SRC emacs-lisp

(require 'org-tempo) ;开启easy template

;; 禁用左尖括号 (setq electric-pair-inhibit-predicate `(lambda (c) (if (char-equal c ?\<) t (,electric-pair-inhibit-predicate c))))

(add-hook 'org-mode-hook (lambda () (setq-local electric-pair-inhibit-predicate `(lambda (c) (if (char-equal c ?\<) t (,electric-pair-inhibit-predicate c))))))

+END_SRC

+BEGIN_SRC emacs-lisp

;; Your code goes here ;; 你的代码写在这里

+END_SRC

你需要将下面的代码放入配置入口文件( =init.el= )中,

+BEGIN_SRC emacs-lisp

(require 'org-install) (require 'ob-tangle) (org-babel-load-file (expand-file-name "zilongshanren.org" user-emacs-directory))

+END_SRC

之后我们需要做的仅仅只是将所有的配置文件放入 Org 模式中的代码块即可,并使用目录 结构来表述你的配置文件再把它保存在与入口文件相同的目录中即可(文件名为 =org-file-name.org= )。Emacs 会提取其中的配置并使其生效。这样做的好处是可以使自 己和他人更直观的,理解你的配置文件或者代码。

这里也给大家推荐一些使用 org-mode 进行配置管理的优秀范例:

https://github.com/trev-dev/emacs

视频地址如下

** 更好的默认设置

在这一节我们会配置我们的编辑器使其有更好的使用体验。整个过程就如同搭积木一般,将 更好的体验建立在已有的功能基础之上。这样的优化使整个过程变得更高效,也更有趣。

下面的代码可以使 Emacs 自动加载外部修改过的文件。

+BEGIN_SRC emacs-lisp

(global-auto-revert-mode 1)

+END_SRC

使用下面的代码可以关闭自动生产的保存文件(之前我们已经关闭过了 Emacs 自动生产的 备份文件了,现在是关闭自动保存文件)。

+BEGIN_SRC emacs-lisp

(setq auto-save-default nil)

+END_SRC

如果你发现你在使用中发现了那些编辑行为与你预期的不相符时,你可以通过搜索引擎去寻 找解决方案然后将其加入你的配置中并打造一个真正属于你的神器!

也许你并不喜欢听到错误时的“哔哔”的警告提示音,使用下面的代码你可以关闭 Emacs 中的警告音,

+BEGIN_SRC emacs-lisp

(setq ring-bell-function 'ignore)

+END_SRC

每一次当 Emacs 需要与你确认某个命令时需要输入 =(yes or no)= 比较麻烦,所有我们可 以使用下面的代码,设置一个别名将其简化为只输入 =(y or n)= 。

+BEGIN_SRC emacs-lisp

(fset 'yes-or-no-p 'y-or-n-p)

+END_SRC

** Macro

** 什么是宏

+begin_src emacs-lisp

(setq my-var 1) (setq my-var (1+ my-var))

(defmacro inc (var) (list 'setq var (list '1+ var)))

(macroexpand '(inc my-var))

(inc my-var)

;;; pass by value (defun inc-2 (var) (setq var (1+ var)))

(inc-2 my-var)

;; Backquote allows you to:

;; quote a list, and ;; selectively evaluate elements of the list (with comma ,), or: ;; splice (eval & spread) the element with ,@

`(a list of ,(+ 2 3) elements)

(setq some-list '(2 3))

`(1 ,@some-list 4 ,@some-list)

+end_src

Backquote:

https://www.gnu.org/software/emacs/manual/html_node/elisp/Backquote.html

** Use-package

*** 安装 Use-package

+begin_src emacs-lisp

(package-install 'use-package)

+end_src

*** 简介

Use-package 是一个宏, 它能让你将一个包的 require 和它的相关的初始化等配置组织 在一起, 避免对同一个包的配置代码散落在不同的文件中.

Use-package 的更多信息参见以下地址: [[https://github.com/jwiegley/use-package][Use-package]]

*** 一些简单的用法

**** 更安全的 require

在 Emacs 中, 当我们要引入一个包时, 通常会使用以下代码:

+BEGIN_SRC emacs-lisp

(require 'package-name)

+END_SRC

但是当 package-name 不在 load-path 中时, 以上代码会抛出错误. 使用 Use-package 可以避免:

+BEGIN_SRC emacs-lisp

(use-package package-name)

+END_SRC

以上代码展开的结果如下:

+BEGIN_SRC emacs-lisp

(if (not (require 'package-name nil 't)) (ignore (message (format "Cannot load %s" 'package-name))))

+END_SRC

可以看到, Use-package 使用 ignore 来避免抛出错误, 这样当某个包不存在时, eamcs 也能够正常启动.

**** 将配置集中

当我们引入某个包时, 有可能需要定义一些与这个包相关的变量, 使用 Use-package 实 现这个需求如下:

+BEGIN_SRC emacs-lisp

(use-package package-name :init (setq my-var1 "xxx") :config (progn (setq my-var2 "xxx") (setq my-var3 "xxx") ) )

+END_SRC

在上例中, init 后的代码在包的 require 之前执行, 如果这段代码出错则跳过包的 require.

config 后的代码在包的 require 之后执行.

initconfig 之后只能接单个表达式语句, 如果需要执行多个语句, 可以用 progn .

**** autoload

使用 require 时会引入这个包, 但是当你的包很多时会影响启动速度. 而使用 autoload 则可以在真正需要这个包时再 require, 提高启动速度, 避免无谓的 require.

使用 Use-package 可以轻松的实现这个功能:

+BEGIN_SRC emacs-lisp

(use-package package-name :commands (global-company-mode) :defer t )

+END_SRC

使用 commands 可以让 package 延迟加载, 如以上代码会首先判断 package 的符号是否 存在, 如果存在则在 package-name 的路径下加载. defer 也可以让 package-name 进行延迟加载.

**** 键绑定

在之前的代码中, 如果我们需要绑定一个键, 需要使用 global-key-binddefine-key 实现, 而使用Use-package 实现更简单:

+BEGIN_SRC emacs-lisp

(use-package company :bind (:map company-active-map ("C-n" . 'company-select-next) ("C-p" . 'company-select-previous)) :init (global-company-mode t) :config (setq company-minimum-prefix-length 1) (setq company-idle-delay 0))

+END_SRC

*** 为什么使用 Use-package

  1. Use-package 能让相关的配置更为集中, 避免配置分散带来的维护困难
  2. Use-package 有完善的错误处理, 使配置代码更为健壮
  3. Spacemacs,DoomEmacs 也大量使用了 Use-package

视频地址如下:

*** 安装 restart-emacs

+begin_src emacs-lisp

(use-package restart-emacs :ensure t)

;;emacs --debug-init

+end_src

删除 company 这个包, 看看会出现什么

*** 自动安装

+begin_src emacs-lisp

(require 'use-package-ensure) (setq use-package-always-ensure t)

;; Setup `use-package' (unless (package-installed-p 'use-package) (package-refresh-contents) (package-install 'use-package))

+end_src

这个选项会自动安装包,但是不会自动更新包到最新版本。

*** 保存光标历史,记住上个命令

+begin_src emacs-lisp

(use-package savehist :ensure 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 saveplace :ensure nil :hook (after-init . save-place-mode))

+end_src

*** 显示文件行、列、大小 ,美化状态栏

+begin_src emacs-lisp

(use-package simple :ensure nil :hook (after-init . size-indication-mode) :init (progn (setq column-number-mode t) ))

;;modeline上显示我的所有的按键和执行的命令 (package-install 'keycast) (add-to-list 'global-mode-string '("" keycast-mode-line)) (keycast-mode t)

;; 这里的执行顺序非常重要,doom-modeline-mode 的激活时机一定要在设置global-mode-string 之后‘ (use-package doom-modeline :ensure t

:init
(doom-modeline-mode t))

+end_src

视频地址如下:

** org mode 简介 *** 查看 orgmode 版本号

C-h v org-version

*** 安装最新版本

+begin_src emacs-lisp

;; 安装org 之前,一定要配置 use-package 不使用内置的org 版本,可以使用本段代码最后面的代码,具体位置可以参考视频 (use-package org :pin melpa :ensure t)

(use-package org-contrib :pin nongnu )

;; 添加新的 nongnu 的源 ("nongnu" . "http://elpa.zilongshanren.com/nongnu/")

;;; 这个配置一定要配置在 use-package 的初始化之前,否则无法正常安装 (assq-delete-all 'org package--builtins) (assq-delete-all 'org package--builtin-versions)

+end_src

*** org todo

+begin_src emacs-lisp

(setq org-todo-keywords (quote ((sequence "TODO(t)" "STARTED(s)" "|" "DONE(d!/!)") (sequence "WAITING(w@/!)" "SOMEDAY(S)" "|" "CANCELLED(c@/!)" "MEETING(m)" "PHONE(p)"))))

;;fix doom modeline :custom-face (mode-line ((t (:height 0.9)))) (mode-line-inactive ((t (:height 0.9))))

(require 'org-checklist) ;; need repeat task and properties (setq org-log-done t) (setq org-log-into-drawer t)

+end_src

*** org agenda

+begin_src emacs-lisp

;; C-c C-s schedule ;; C-c C-d deadline (global-set-key (kbd "C-c a") 'org-agenda) (setq org-agenda-files '("~/gtd.org")) (setq org-agenda-span 'day)

+end_src

*** org capture

+begin_src emacs-lisp

(setq org-capture-templates '(("t" "Todo" entry (file+headline "~/gtd.org" "Workspace") "* TODO [#B] %?\n %i\n %U" :empty-lines 1)))

(global-set-key (kbd "C-c r") 'org-capture)

+end_src

*** org effect 在 agenda view 中, 按 e 键可以设置 effort, 按 _ 可以过滤指定 effort 的 heading

*** org tags 在 agenda view 中, 按 : 可以按照 tag 名称来过滤 todo 的 heading (tips: 这里快捷键都可以通过 M-x 来查看)

*** org priority in agenda view ,

+begin_src emacs-lisp

(setq org-agenda-custom-commands '(("c" "重要且紧急的事" ((tags-todo "+PRIORITY=\"A\""))) ;; ...other commands here ))

+end_src

** 参考资料 https://www.cnblogs.com/Open_Source/archive/2011/07/17/2108747.html

视频地址如下:

使用 org mode 写博客,然后托管在 Github 上面,我们可以打造一个非常好用便捷且免费的写作系统。 ** 安装 hugo 在 windows 上面安装 hugo 有多种方法,本文使用 chocolatey 包管理器。

其他操作系统以及 windows 平台更多的安装方法参考以下链接: https://gohugo.io/getting-started/installing/

*** 安卓 choco 包管理器 使用 powershell 来安装(如果你的系统没有 powershell, 需要首先安装 powershell).

使用管理员权限打开 powershell

+begin_src sh

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

+end_src

安装完后,在 powershell 中输入 choco, 你会得到如下截图则说明安装成功:

+DOWNLOADED: screenshot @ 2022-10-16 20:36:02

[[file:img/2022-10-16_20-36-02_screenshot.jpg]]

*** 安装 hugo

+begin_src sh

choco install hugo --confirm

+end_src

** 新建博客站点

+begin_src sh

hugo new site blog

+end_src

使用 git 来管理你的博客系统:

+begin_src sh

cd blog git init git add . git commit -m "first commit"

+end_src

** 安装 hugo 主题 even 我们使用 git submodule 来安装:

+begin_src sh

git submodule add https://github.com/olOwOlo/hugo-theme-even themes/even

+end_src

** 测试站点

拷贝 even 主题中的 examplesite 里面的内容到 testblog:

在命令行中输入 hugo server, 此命令会自动生成博客内容到 public 目录,同时会在本地启动一个 http 服务器供我们来预览博客。 关于 even 主题更多地使用请参考官方主页:

注意: 如果要显示草稿内容,需要使用 hugo server -D

https://github.com/olOwOlo/hugo-theme-even

** 使用 org mode 来写博客

首先我们需要安装 https://github.com/kaushalmodi/ox-hugo

+begin_src emacs-lisp

(use-package ox-hugo :ensure t ;Auto-install the package from Melpa :pin melpa ;`package-archives' should already have ("melpa" . "https://melpa.org/packages/") :after ox)

+end_src

ox-hugo 有两种方式来写博客。

使用 org capture 来新建博客:(注意 elisp 中 blog 文件的路径要替换成你自己的)

+begin_src emacs-lisp

(with-eval-after-load 'org-capture (defun org-hugo-new-subtree-post-capture-template () "Returns org-capture' template string for new Hugo post. Seeorg-capture-templates' for more information." (let ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title (fname (org-hugo-slug title))) (mapconcat #'identity `( ,(concat " TODO " title) ":PROPERTIES:" ,(concat ":EXPORT_FILE_NAME: " fname) ":END:" "\n\n") ;Place the cursor here finally "\n")))

(add-to-list 'org-capture-templates
             '("h"                ;`org-capture' binding + h
               "Hugo post"
               entry
               ;; It is assumed that below file is present in `org-directory'
               ;; and that it has a "Blog Ideas" heading. It can even be a
               ;; symlink pointing to the actual location of all-posts.org!
               (file+headline "c:/Users/lionqu/blog/all-blog.org" "Blog Ideas")
               (function org-hugo-new-subtree-post-capture-template))))

+end_src

配置 all-blog.org, 让它在保存内容的时候自动生成博客内容。

https://ox-hugo.scripter.co/doc/auto-export-on-saving/

** 配置 github 和 github workflow 来自动发布博客

  1. 新建一个 github 仓库
  2. 添加 remote 并 push 到远程
  3. 添加 github workflow

视频地址如下:

** 安装 Emacs 29 (pretest 版本下载地址) https://alpha.gnu.org/gnu/emacs/pretest/windows/emacs-29/?C=M;O=D 修复一个运行报错:

+begin_src emacs-lisp

                                      ;  (global-linum-mode 1)

(global-display-line-numbers-mode t) ;修改成这个来显示行号,性能更好

+end_src

** 安装 mysys2

  1. 下载 https://www.msys2.org/
  2. 安装 C/C++ 必要的软件工具

    +begin_src sh

    pacman -Syu pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-gdb make mingw-w64-x86_64-clang mingw-w64-x86_64-clang-tools-extra

    +end_src

  3. 设置环境变量

    +begin_src sh

    C:\msys64\mingw64\bin C:\msys64\usr\bin

    +end_src

** 安装 eglot(Emacs 29 自带 eglot)

+begin_src emacs-lisp

(require 'eglot) (add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd")) (add-hook 'c-mode-hook #'eglot-ensure) (add-hook 'c++-mode-hook #'eglot-ensure)

+end_src

** 一键运行代码

  1. 安装 quickrun:

    +begin_src emacs-lisp

    (use-package quickrun :ensure t :commands (quickrun) :init (quickrun-add-command "c++/c1z" '((:command . "g++") (:exec . ("%c -std=c++1z %o -o %e %s" "%e %a")) (:remove . ("%e"))) :default "c++"))

    +end_src

  2. 自定义快捷键

    +begin_src emacs-lisp

    (global-set-key (kbd "") 'quickrun)

    +end_src

** 使用 gdb 调试

  1. 运行 M-x compile, 输入 g++ -g -o test.o test.cpp
  2. 使用 M-x gud-gdb 输入 gdb ./test.o
  3. 常用调试命令 | name | function | |-----------------+----------------------------------------------------| | list | 显示源代码 | | break | 新增断点, break main, break 12(行号) | | info | 查看断点或者局部变量信息 info breakpoints, info locals | | run | 开始调试 | | next | 类似 step over | | step | 跳转到函数内部 | | continue | 继续运行到下一个断点 | | quit | 退出调试 | | watch | 内存断点 | | display | 类似 IDE 里面的 watch 功能 | | break 11 if xxx | 条件断点 |

视频地址如下:

** 安装 Evil *** Install from melpa

+begin_src emacs-lisp

(use-package evil :ensure t :init (setq evil-want-keybinding nil) (setq evil-want-C-u-scroll t) (evil-mode)

;; https://emacs.stackexchange.com/questions/46371/how-can-i-get-ret-to-follow-org-mode-links-when-using-evil-mode
(with-eval-after-load 'evil-maps
  (define-key evil-motion-state-map (kbd "RET") nil))
)

+end_src

*** Install undo tree

+begin_src emacs-lisp

(use-package undo-tree :diminish :init (global-undo-tree-mode 1) (setq undo-tree-auto-save-history nil) (evil-set-undo-system 'undo-tree))

+end_src

Modes and States ** Normal State (N) This is the default “resting state” of Evil, in which the main body of vi bindings are defined. Insert State (I) This is the state for insertion of text, where non-modified keys will insert the corresponding character in the buffer. Emacs State (E) A state that as closely as possible mimics default Emacs behaviour, by eliminating all vi bindings, except for C-z, to re-enter normal state. Visual State (V) A state for selecting text regions. Motions are available for modifying the selected region, and operators are available for acting on it. Motion State (M) A special state useful for buffers that are read-only, where motions are available but editing operations are not. Replace State (R) A special state mostly similar to insert state, except it replaces text instead of inserting. * Operator-Pending State (O) A special state entered after launching an operator, but before specifying the corresponding motion or text object. * Evil 基础用法 增删改查 (text-obj, commands, replace ) * 如果对 evil 不太熟悉,还可以使用这个插件来练习 evil 的使用: https://github.com/clsty/evil-tutor-sc * 自定义快捷键 定义不同 state 下面的快捷键

+begin_src emacs-lisp

(setcdr evil-insert-state-map nil) (define-key evil-insert-state-map [escape] 'evil-normal-state)

(define-key evil-normal-state-map (kbd "[ SPC") (lambda () (interactive) (evil-insert-newline-above) (forward-line))) (define-key evil-normal-state-map (kbd "] SPC") (lambda () (interactive) (evil-insert-newline-below) (forward-line -1)))

(define-key evil-normal-state-map (kbd "[ b") 'previous-buffer) (define-key evil-normal-state-map (kbd "] b") 'next-buffer) (define-key evil-motion-state-map (kbd "[ b") 'previous-buffer) (define-key evil-motion-state-map (kbd "] b") 'next-buffer)

(evil-define-key 'normal dired-mode-map (kbd "") 'dired-find-alternate-file (kbd "C-k") 'dired-up-directory "`" 'dired-open-term "o" 'dired-find-file-other-window "s" 'hydra-dired-quick-sort/body "z" 'dired-get-size "!" 'zilongshanren/do-shell-and-copy-to-kill-ring ")" 'dired-omit-mode)

+end_src

*** spacemacs like 快捷键如何定义

+begin_src emacs-lisp

(use-package general :init (with-eval-after-load 'evil (general-add-hook 'after-init-hook (lambda (&rest _) (when-let ((messages-buffer (get-buffer "Messages"))) (with-current-buffer messages-buffer (evil-normalize-keymaps)))) nil nil t))

(general-create-definer global-definer
  :keymaps 'override
  :states '(insert emacs normal hybrid motion visual operator)
  :prefix "SPC"
  :non-normal-prefix "C-SPC")

(defmacro +general-global-menu! (name infix-key &rest body)
  "Create a definer named +general-global-NAME wrapping global-definer.

Create prefix map: +general-global-NAME. Prefix bindings in BODY with INFIX-KEY." (declare (indent 2)) `(progn (general-create-definer ,(intern (concat "+general-global-" name)) :wrapping global-definer :prefix-map ',(intern (concat "+general-global-" name "-map")) :infix ,infix-key :wk-full-keys nil "" '(:ignore t :which-key ,name)) (,(intern (concat "+general-global-" name)) ,@body)))

(general-create-definer global-leader
  :keymaps 'override
  :states '(emacs normal hybrid motion visual operator)
  :prefix ","
  "" '(:ignore t :which-key (lambda (arg) `(,(cadr (split-string (car arg) " ")) . ,(replace-regexp-in-string "-mode$" "" (symbol-name major-mode)))))))

(use-package general :init (global-definer "!" 'shell-command "SPC" 'execute-extended-command "'" 'vertico-repeat "+" 'text-scale-increase "-" 'text-scale-decrease "u" 'universal-argument "hdf" 'describe-function "hdv" 'describe-variable "hdk" 'describe-key )

(+general-global-menu! "buffer" "b"
  "d" 'kill-current-buffer
  "b" '(consult-buffer :which-key "consult buffer")
  "B" 'switch-to-buffer
  "p" 'previous-buffer
  "R" 'rename-buffer
  "M" '((lambda () (interactive) (switch-to-buffer "*Messages*"))
        :which-key "messages-buffer")
  "n" 'next-buffer
  "i" 'ibuffer
  "f" 'my-open-current-directory
  "k" 'kill-buffer
  "y" 'copy-buffer-name
  "K" 'kill-other-buffers)

+end_src

参考我的配置: https://github.com/zilongshanren/emacs.d/blob/eglot/lisp/init-keybindings.el ** 相关插件列表(推荐我自己经常用的) *** evil-anzu

+begin_src emacs-lisp

(use-package evil-anzu :ensure t :after evil :diminish :demand t :init (global-anzu-mode t))

+end_src

*** evil-collections

+begin_src emacs-lisp

(use-package evil-collection :ensure t :demand t :config (setq evil-collection-mode-list (remove 'lispy evil-collection-mode-list)) (evil-collection-init)

(cl-loop for (mode . state) in
         '((org-agenda-mode . normal)
           (Custom-mode . emacs)
           (eshell-mode . emacs)
           (makey-key-mode . motion))
         do (evil-set-initial-state mode state))

+end_src

*** evil-surround

+begin_src emacs-lisp

(use-package evil-surround :ensure t :init (global-evil-surround-mode 1))

+end_src

*** evil-nerd-commenter

+begin_src emacs-lisp

(use-package evil-nerd-commenter :init (define-key evil-normal-state-map (kbd ",/") 'evilnc-comment-or-uncomment-lines) (define-key evil-visual-state-map (kbd ",/") 'evilnc-comment-or-uncomment-lines) )

+end_src

*** evil-snipe

+begin_src emacs-lisp

(use-package evil-snipe :ensure t :diminish :init (evil-snipe-mode +1) (evil-snipe-override-mode +1))

+end_src

*** evil-matchit

+begin_src emacs-lisp

(use-package evil-matchit :ensure :init (global-evil-matchit-mode 1))

+end_src

** 高阶用法(自定义 text-obj 或者 自定义 commands)

  1. https://github.com/noctuid/evil-guide#modes
  2. https://evil.readthedocs.io/en/latest/overview.html

视频地址如下:

** Some minor fix:

+begin_src emacs-lisp

;; make c-j/c-k work in vertico selection (define-key vertico-map (kbd "C-j") 'vertico-next) (define-key vertico-map (kbd "C-k") 'vertico-previous)

;; make consult-ripgrep work (add-to-list 'process-coding-system-alist '("[rR][gG]" . (utf-8-dos . windows-1251-dos)))

+end_src

** 多光标操作 iedit & evil-multiedit

+begin_src emacs-lisp

(use-package iedit :ensure t :init (setq iedit-toggle-key-default nil) :config (define-key iedit-mode-keymap (kbd "M-h") 'iedit-restrict-function) (define-key iedit-mode-keymap (kbd "M-i") 'iedit-restrict-current-line))

(use-package evil-multiedit :ensure t :commands (evil-multiedit-default-keybinds) :init (evil-multiedit-default-keybinds))

+end_src

** expand-region

+begin_src emacs-lisp

(use-package expand-region :config (defadvice er/prepare-for-more-expansions-internal (around helm-ag/prepare-for-more-expansions-internal activate) ad-do-it (let ((new-msg (concat (car ad-return-value) ", H to highlight in buffers" ", / to search in project, " "e iedit mode in functions" "f to search in files, " "b to search in opened buffers")) (new-bindings (cdr ad-return-value))) (cl-pushnew '("H" (lambda () (interactive) (call-interactively 'zilongshanren/highlight-dwim))) new-bindings) (cl-pushnew '("/" (lambda () (interactive) (call-interactively 'my/search-project-for-symbol-at-point))) new-bindings) (cl-pushnew '("e" (lambda () (interactive) (call-interactively 'evil-multiedit-match-all))) new-bindings) (cl-pushnew '("f" (lambda () (interactive) (call-interactively 'find-file))) new-bindings) (cl-pushnew '("b" (lambda () (interactive) (call-interactively 'consult-line))) new-bindings) (setq ad-return-value (cons new-msg new-bindings)))))

+end_src

添加一个快捷键,让标记和搜索功能更方便:

+begin_src emacs-lisp

;;;###autoload (defun my/search-project-for-symbol-at-point () (interactive) (if (use-region-p) (progn (consult-ripgrep (project-root (project-current)) (buffer-substring (region-beginning) (region-end))))))

(global-definer "hc" 'zilongshanren/clearn-highlight "hH" 'zilongshanren/highlight-dwim "v" 'er/expand-region )

+end_src

** interactive replace

+begin_src emacs-lisp

(defun zilongshanren/evil-quick-replace (beg end ) (interactive "r") (when (evil-visual-state-p) (evil-exit-visual-state) (let ((selection (regexp-quote (buffer-substring-no-properties beg end)))) (setq command-string (format "%%s /%s//g" selection)) (minibuffer-with-setup-hook (lambda () (backward-char 2)) (evil-ex command-string)))))

(define-key evil-visual-state-map (kbd "C-r") 'zilongshanren/evil-quick-replace)

+end_src

** 安装 quelpa 插件

+begin_src emacs-lisp

(use-package quelpa)

(unless (package-installed-p 'quelpa-use-package) (quelpa '(quelpa-use-package :fetcher git :url "https://github.com/quelpa/quelpa-use-package.git")))

(use-package quelpa-use-package :init (setq quelpa-use-package-inhibit-loading-quelpa t) :demand t)

+end_src

** symbol-overlay & highlight-global

+begin_src emacs-lisp

(defun zilongshanren/highlight-dwim () (interactive) (if (use-region-p) (progn (highlight-frame-toggle) (deactivate-mark)) (symbol-overlay-put)))

(defun zilongshanren/clearn-highlight () (interactive) (clear-highlight-frame) (symbol-overlay-remove-all))

(use-package symbol-overlay :config (define-key symbol-overlay-map (kbd "h") 'nil))

(use-package highlight-global :ensure nil :commands (highlight-frame-toggle) :quelpa (highlight-global :fetcher github :repo "glen-dai/highlight-global") :config (progn (setq-default highlight-faces '(('hi-red-b . 0) ('hi-aquamarine . 0) ('hi-pink . 0) ('hi-blue-b . 0)))))

+end_src

视频地址如下:

正式版本 Emacs 29 http://ftp.gnu.org/gnu/windows/emacs/emacs-29/ 安装 treesit-auto 插件 https://github.com/renzmann/treesit-auto

+begin_src emacs-lisp

(use-package treesit-auto :demand t :config (setq treesit-auto-install 'prompt) (global-treesit-auto-mode))

+end_src

** 配置 fontlock level

+begin_src emacs-lisp

(setq treesit-font-lock-level 4)

+end_src

** 跳转函数列表 consult-imenu

+begin_src emacs-lisp

(+general-global-menu! "search" "s" "j" 'consult-imenu "p" 'consult-ripgrep "k" 'consult-keep-lines "f" 'consult-focus-lines)

+end_src

** 查找定义和引用 使用 ctrl-o 返回

+begin_src emacs-lisp

xref-find-references xref-find-definitions

+end_src

** 添加 snippets 支持

+begin_src emacs-lisp

(use-package yasnippet :ensure t :hook ((prog-mode . yas-minor-mode) (org-mode . yas-minor-mode)) :init :config (progn (setq hippie-expand-try-functions-list '(yas/hippie-try-expand try-complete-file-name-partially try-expand-all-abbrevs try-expand-dabbrev try-expand-dabbrev-all-buffers try-expand-dabbrev-from-kill try-complete-lisp-symbol-partially try-complete-lisp-symbol))))

(use-package yasnippet-snippets :ensure t :after yasnippet)

+end_src

** 在头文件和源文件之间进行跳转

+begin_src emacs-lisp

ff-find-related-file

+end_src

** 参考链接

视频地址如下:

** Evil 的窗口选择操作

+begin_src emacs-lisp

(use-package window-numbering :init :hook (after-init . window-numbering-mode))

+end_src

*** [[https://github.com/sabof/es-windows][es-windows]]

+begin_src emacs-lisp

(use-package es-windows :ensure t)

+end_src

*** [[https://github.com/nschum/window-numbering.el][Buffer move]]

+begin_src emacs-lisp

(use-package buffer-move :ensure t)

+end_src

*** [[https://github.com/dpsutton/resize-mode][Resize windows]]

+begin_src emacs-lisp

(use-package resize-window :ensure t :init (defvar resize-window-dispatch-alist '((?n resize-window--enlarge-down " Resize - Expand down" t) (?p resize-window--enlarge-up " Resize - Expand up" t) (?f resize-window--enlarge-horizontally " Resize - horizontally" t) (?b resize-window--shrink-horizontally " Resize - shrink horizontally" t) (?r resize-window--reset-windows " Resize - reset window layout" nil) (?w resize-window--cycle-window-positive " Resize - cycle window" nil) (?W resize-window--cycle-window-negative " Resize - cycle window" nil) (?2 split-window-below " Split window horizontally" nil) (?3 split-window-right " Slit window vertically" nil) (?0 resize-window--delete-window " Delete window" nil) (?K resize-window--kill-other-windows " Kill other windows (save state)" nil) (?y resize-window--restore-windows " (when state) Restore window configuration" nil) (?? resize-window--display-menu " Resize - display menu" nil)) "List of actions for `resize-window-dispatch-default. Main data structure of the dispatcher with the form: (char function documentation match-capitals)"))

+end_src

*** Winner (builtin)

+begin_src emacs-lisp

(use-package winner :ensure nil :commands (winner-undo winner-redo) :hook (after-init . winner-mode) :init (setq winner-boring-buffers '("Completions" "Compile-Log" "inferior-lisp" "Fuzzy Completions" "Apropos" "Help" "cvs" "Buffer List" "Ibuffer" "esh command on file")))

+end_src

*** [[https://github.com/karthink/popper][Popper]]

+begin_src emacs-lisp

(use-package popper :defines popper-echo-dispatch-actions :commands popper-group-by-directory :bind (:map popper-mode-map ("s-" . popper-toggle-latest) ("s-o" . popper-cycle) ("M-" . popper-toggle-type)) :hook (emacs-startup . popper-mode) :init (setq popper-reference-buffers '("\Messages\" "Output\$" "\Pp Eval Output\$" "\Compile-Log\*" "\Completions\" "\Warnings\" "\Flymake diagnostics.\" "\Async Shell Command\" "\Apropos\" "\Backtrace\" "\prodigy\" "\*Calendar\" "\Embark Actions\" "\Finder\" "\Kill Ring\" "\Embark Export:.\" "\*Edit Annotation.\" "\*Flutter\" bookmark-bmenu-mode lsp-bridge-ref-mode comint-mode compilation-mode help-mode helpful-mode tabulated-list-mode Buffer-menu-mode occur-mode gnus-article-mode devdocs-mode grep-mode occur-mode rg-mode deadgrep-mode ag-mode pt-mode ivy-occur-mode ivy-occur-grep-mode process-menu-mode list-environment-mode cargo-process-mode youdao-dictionary-mode osx-dictionary-mode fanyi-mode

        "^\\*eshell.*\\*.*$" eshell-mode
        "^\\*shell.*\\*.*$"  shell-mode
        "^\\*terminal.*\\*.*$" term-mode
        "^\\*vterm.*\\*.*$"  vterm-mode

        "\\*DAP Templates\\*$" dap-server-log-mode
        "\\*ELP Profiling Restuls\\*" profiler-report-mode
        "\\*Flycheck errors\\*$" " \\*Flycheck checker\\*$"
        "\\*Paradox Report\\*$" "\\*package update results\\*$" "\\*Package-Lint\\*$"
        "\\*[Wo]*Man.*\\*$"
        "\\*ert\\*$" overseer-buffer-mode
        "\\*gud-debug\\*$"
        "\\*lsp-help\\*$" "\\*lsp session\\*$"
        "\\*quickrun\\*$"
        "\\*tldr\\*$"
        "\\*vc-.*\\*$"
        "\\*eldoc\\*"
        "^\\*elfeed-entry\\*$"
        "^\\*macro expansion\\**"

        "\\*Agenda Commands\\*" "\\*Org Select\\*" "\\*Capture\\*" "^CAPTURE-.*\\.org*"
        "\\*Gofmt Errors\\*$" "\\*Go Test\\*$" godoc-mode
        "\\*docker-containers\\*" "\\*docker-images\\*" "\\*docker-networks\\*" "\\*docker-volumes\\*"
        "\\*prolog\\*" inferior-python-mode inf-ruby-mode swift-repl-mode
        "\\*rustfmt\\*$" rustic-compilation-mode rustic-cargo-clippy-mode
        rustic-cargo-outdated-mode rustic-cargo-test-moed))

(when (display-grayscale-p)
  (setq popper-mode-line
        '(:eval
          (concat
           (propertize " " 'face 'mode-line-emphasis)
           (all-the-icons-octicon "pin" :height 0.9 :v-adjust 0.0 :face 'mode-line-emphasis)
           (propertize " " 'face 'mode-line-emphasis)))))

(setq popper-echo-dispatch-actions t)
(setq popper-group-function nil)
:config
(popper-echo-mode 1)

(with-no-warnings
  (defun my-popper-fit-window-height (win)
    "Determine the height of popup window WIN by fitting it to the buffer's content."
    (fit-window-to-buffer
     win
     (floor (frame-height) 3)
     (floor (frame-height) 3)))
  (setq popper-window-height #'my-popper-fit-window-height)

  (defun popper-close-window-hack (&rest _)
    "Close popper window via `C-g'."
    ;; `C-g' can deactivate region
    (when (and (called-interactively-p 'interactive)
               (not (region-active-p))
               popper-open-popup-alist)
      (let ((window (caar popper-open-popup-alist)))
        (when (window-live-p window)
          (delete-window window)))))
  (advice-add #'keyboard-quit :before #'popper-close-window-hack)))

+end_src

** 按键绑定

+begin_src emacs-lisp

(global-definer ;; 这里是其他的快捷键 "0" 'select-window-0 "1" 'select-window-1 "2" 'select-window-2 "3" 'select-window-3 "4" 'select-window-4 "5" 'select-window-5)

(+general-global-menu! "window" "w" "/" 'split-window-right "-" 'split-window-below "m" 'delete-other-windows "u" 'winner-undo "z" 'winner-redo "w" 'esw/select-window "s" 'esw/swap-two-windows "d" 'esw/delete-window "=" 'balance-windows-area "r" 'esw/move-window "x" 'resize-window "H" 'buf-move-left "L" 'buf-move-right "J" 'buf-move-down "K" 'buf-move-up)

+end_src

视频地址如下:

Awesome Tab 的问题 偏爱使用 Emacs 内置 package 使用内置的 tab-bar-mode

+begin_src emacs-lisp

(use-package tab-bar :ensure nil :init (tab-bar-mode t) (setq tab-bar-new-tab-choice "scratch") ;; buffer to show in new tabs (setq tab-bar-close-button-show nil) ;; hide tab close / X button (setq tab-bar-show 1) ;; hide bar if <= 1 tabs open (setq tab-bar-format '(tab-bar-format-tabs tab-bar-separator))

(custom-set-faces
 '(tab-bar ((t (:inherit mode-line))))
 '(tab-bar-tab ((t (:inherit mode-line :foreground "#993644"))))
 '(tab-bar-tab-inactive ((t (:inherit mode-line-inactive :foreground "black")))))

(defvar ct/circle-numbers-alist
  '((0 . "⓪")
    (1 . "①")
    (2 . "②")
    (3 . "③")
    (4 . "④")
    (5 . "⑤")
    (6 . "⑥")
    (7 . "⑦")
    (8 . "⑧")
    (9 . "⑨"))
  "Alist of integers to strings of circled unicode numbers.")

(defun ct/tab-bar-tab-name-format-default (tab i)
  (let ((current-p (eq (car tab) 'current-tab))
        (tab-num (if (and tab-bar-tab-hints (< i 10))
                     (alist-get i ct/circle-numbers-alist) "")))
    (propertize
     (concat tab-num
             " "
             (alist-get 'name tab)
             (or (and tab-bar-close-button-show
                      (not (eq tab-bar-close-button-show
                               (if current-p 'non-selected 'selected)))
                      tab-bar-close-button)
                 "")
             " ")
     'face (funcall tab-bar-tab-face-function tab))))
(setq tab-bar-tab-name-format-function #'ct/tab-bar-tab-name-format-default)
(setq tab-bar-tab-hints t))

+end_src

** 工作区间

+begin_src emacs-lisp

(use-package tabspaces ;; use this next line only if you also use straight, otherwise ignore it. :hook (after-init . tabspaces-mode) ;; use this only if you want the minor-mode loaded at startup. :commands (tabspaces-switch-or-create-workspace tabspaces-open-or-create-project-and-workspace) :custom (tabspaces-use-filtered-buffers-as-default t) (tabspaces-default-tab "Default") (tabspaces-remove-to-default t) (tabspaces-include-buffers '("scratch")) ;; maybe slow (tabspaces-session t) (tabspaces-session-auto-restore t) :config ;; Filter Buffers for Consult-Buffer

(with-eval-after-load 'consult
  ;; hide full buffer list (still available with "b" prefix)
  (consult-customize consult--source-buffer :hidden nil :default nil)
  ;; set consult-workspace buffer list
  (defvar consult--source-workspace
    (list :name "Workspace Buffers"
          :narrow ?w
          :history 'buffer-name-history
          :category 'buffer
          :state #'consult--buffer-state
          :default t
          :items (lambda () (consult--buffer-query
                             :predicate #'tabspaces--local-buffer-p
                             :sort 'visibility
                             :as #'buffer-name)))

    "Set workspace buffer list for consult-buffer.")
  (add-to-list 'consult-buffer-sources 'consult--source-workspace)))

+end_src

** 按键绑定

+begin_src emacs-lisp

(+general-global-menu! "layout" "l" "l" 'tabspaces-switch-or-create-workspace "L" 'tabspaces-restore-session "p" 'tabspaces-open-or-create-project-and-workspace "f" 'tabspaces-project-switch-project-open-file "s" 'tabspaces-save-session "B" 'tabspaces-switch-buffer-and-tab "b" 'tabspaces-switch-to-buffer "R" 'tab-rename "TAB" 'tab-bar-switch-to-recent-tab "r" 'tabspaces-remove-current-buffer "k" 'tabspaces-close-workspace)

+end_src

** 工作流介绍

视频地址如下:

** Org Download *** how to install

+begin_src emacs-lisp

(use-package org-download :ensure t :demand t :after org :config (add-hook 'dired-mode-hook 'org-download-enable) (setq org-download-screenshot-method "powershell -c Add-Type -AssemblyName System.Windows.Forms;$image = [Windows.Forms.Clipboard]::GetImage();$image.Save('%s', [System.Drawing.Imaging.ImageFormat]::Png)") (defun org-download-annotate-default (link) "Annotate LINK with the time of download." (make-string 0 ?\s))

(setq-default org-download-heading-lvl nil
              org-download-image-dir "./img"
              ;; org-download-screenshot-method "screencapture -i %s"
              org-download-screenshot-file (expand-file-name "screenshot.jpg" temporary-file-directory)))

+end_src

*** Change image size

+begin_src

+ATTR_HTML: :width 1000px

+end_src

use yasnippets ** Org Protocol * How to config ** 1. Step 1: Get emacsclient to work **** 2. 生成 org-protocol.reg

+begin_src sh

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\org-protocol] "URL Protocol"="" @="URL:Org Protocol"

[HKEY_CLASSES_ROOT\org-protocol\shell]

[HKEY_CLASSES_ROOT\org-protocol\shell\open]

[HKEY_CLASSES_ROOT\org-protocol\shell\open\command] @="\"C:\Program Files\Emacs\emacs-29.1\bin\emacsclientw.exe\" \"%1\""

+end_src

**** 3. add org templates

+begin_src emacs-lisp

(setq org-agenda-file-note (expand-file-name "~/notes.org")) (setq org-capture-templates '( ("x" "Web Collections" entry (file+headline org-agenda-file-note "Web") "* %U %:annotation\n\n%:initial\n\n%?") ))

+end_src

**** 4. add chrome bookmark

+begin_src sh

javascript:location.href='org-protocol://capture?template=x&url=%27+encodeURIComponent(location.href)+%27&title=%27+encodeURIComponent(document.title)+%27&body=%27+encodeURIComponent(function(){var html = "";var sel = window.getSelection();if (sel.rangeCount) {var container = document.createElement("div");for (var i = 0, len = sel.rangeCount; i < len; ++i) {container.appendChild(sel.getRangeAt(i).cloneContents());}html = container.innerHTML;}var dataDom = document.createElement(%27div%27);dataDom.innerHTML = html;dataDom.querySelectorAll(%27a%27).forEach(function(item, idx) {console.log(%27find a link%27);var url = new URL(item.href, window.location.href).href;var content = item.innerText;item.innerText = %27[[%27+url+%27][%27+content+%27]]%27;});[%27p%27, %27h1%27, %27h2%27, %27h3%27, %27h4%27].forEach(function(tag, idx){dataDom.querySelectorAll(tag).forEach(function(item, index) {var content = item.innerHTML.trim();if (content.length > 0) {item.innerHTML = content + %27 ';}});});return dataDom.innerText.trim();}())

+end_src

**** 6. return follow link

+begin_src emacs-lisp

(org-return-follows-link t)

+end_src

** Spell checking *** Install emacs packages

+begin_src emacs-lisp

(use-package flyspell-correct
  :ensure t
  :init)

(use-package ispell
  :ensure nil
  :init
  (setq ispell-program-name "aspell")
  (dolist (hook '(text-mode-hook))
    (add-hook hook (lambda () (flyspell-mode 1))))
  (setq ispell-personal-dictionary "c:/msys64/mingw64/lib/aspell-0.60/en_GB")

)

  (define-key evil-insert-state-map (kbd "C-;") 'flyspell-correct-previous)

+end_src

*** Install spell

+begin_src sh

pacman -S mingw64/mingw-w64-x86_64-aspell pacman -S mingw64/mingw-w64-x86_64-aspell-en

+end_src

*** Configuration

  1. add "*C:\msys64\mingw64\bin" to to PATH
  2. Error: The file "c:\msys64\mingw64/lib/aspell-0.60/en_US" can not be opened for reading.

** Fanyi Dictionary *** How to install

+begin_src emacs-lisp

(use-package fanyi :ensure t :custom (fanyi-providers '(;; 海词 ;; fanyi-haici-provider ;; 有道同义词词典 fanyi-youdao-thesaurus-provider ;; Etymonline fanyi-etymon-provider ;; Longman ;; fanyi-longman-provider) )))

+end_src

** Some useful keybindings

+begin_src emacs-lisp

C-RET for creating new headlines M-RET for creating new lists M-left/right/up/down

+end_src

视频地址如下:

** 安装 Org Oram 在 HOME 新建一个 org 目录夹,用来存储你的 org roam 笔记。

+begin_src emacs-lisp

(use-package org-roam :ensure t :custom (org-roam-directory (file-truename "~/org")) :bind (("C-c n l" . org-roam-buffer-toggle) ("C-c n f" . org-roam-node-find) ("C-c n g" . org-roam-graph) ("C-c n i" . org-roam-node-insert) ("C-c n c" . org-roam-capture) ;; Dailies ("C-c n j" . org-roam-dailies-capture-today)) :config ;; If you're using a vertical completion framework, you might want a more informative completion interface (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag))) (org-roam-db-autosync-mode) ;; If using org-roam-protocol (require 'org-roam-protocol))

+end_src

** Org roam 基本使用 *** 安装 corfu 来进行补全

+begin_src emacs-lisp

(use-package corfu :init (progn (setq corfu-auto t) (setq corfu-cycle t) (setq corfu-quit-at-boundary t) (setq corfu-quit-no-match t) (setq corfu-preview-current nil) (setq corfu-min-width 80) (setq corfu-max-width 100) (setq corfu-auto-delay 0.2) (setq corfu-auto-prefix 1) (setq corfu-on-exact-match nil) (global-corfu-mode) ))

+end_src

新建笔记和查找笔记 C-c n f 建立笔记引用 [[]] *** 查找引用 org-return-follows-link t c-c n l (org-roam-buffer-toggle)

* 已有的 headline 转换为一个节点 org-id-get-create refile node 直接从 github 安装插件

+begin_src emacs-lisp

(require 'cl-lib) (require 'use-package-core)

(cl-defun slot/vc-install (&key (fetcher "github") repo name rev backend) (let* ((url (format "https://www.%s.com/%s" fetcher repo)) (iname (when name (intern name))) (package-name (or iname (intern (file-name-base repo))))) (unless (package-installed-p package-name) (package-vc-install url iname rev backend))))

(defvar package-vc-use-package-keyword :vc)

(defun package-vc-use-package-set-keyword () (unless (member package-vc-use-package-keyword use-package-keywords) (setq use-package-keywords (let* ((pos (cl-position :unless use-package-keywords)) (head (cl-subseq use-package-keywords 0 (+ 1 pos))) (tail (nthcdr (+ 1 pos) use-package-keywords))) (append head (list package-vc-use-package-keyword) tail)))))

(defun use-package-normalize/:vc (name-symbol keyword args) (let ((arg (car args))) (pcase arg ((or nilt) (list name-symbol)) ((pred symbolp) args) ((pred listp) (cond ((listp (car arg)) arg) ((string-match "^:" (symbol-name (car arg))) (cons name-symbol arg)) ((symbolp (car arg)) args))) (_ nil))))

(defun use-package-handler/:vc (name-symbol keyword args rest state) (let ((body (use-package-process-keywords name-symbol rest state))) ;; This happens at macro expansion time, not when the expanded code is ;; compiled or evaluated. (if args (use-package-concat `((unless (package-installed-p ',(pcase (car args) ((pred symbolp) (car args)) ((pred listp) (car (car args))))) (apply #'slot/vc-install ',(cdr args)))) body) body)))

(defun package-vc-use-package-override-:ensure (func name-symbol keyword ensure rest state) (let ((ensure (if (plist-member rest :vc) nil ensure))) (funcall func name-symbol keyword ensure rest state)))

(defun package-vc-use-package-activate-advice () (advice-add 'use-package-handler/:ensure :around

'package-vc-use-package-override-:ensure))

(defun package-vc-use-package-deactivate-advice () (advice-remove 'use-package-handler/:ensure

'package-vc-use-package-override-:ensure))

;; register keyword on require (package-vc-use-package-set-keyword)

+end_src

** Org roam UI

+begin_src emacs-lisp

(use-package org-roam-ui :vc (:fetcher "github" :repo "org-roam/org-roam-ui"))

+end_src

视频地址如下:

配置有问题,启动不了怎么办? emacs-debug --init 不能安装包,提示签名错误 Failed to verify signature archive-contents.sig

+begin_src emacs-lisp

(setq package-check-signature nil)

+end_src

[[https://emacs-china.org/t/failed-to-verify-signature-archive-contents-sig/20508/5][Failed to verify signature archive-contents.sig - Emacs-general - Emacs China]] ** a pax extended header, 包解压失败,可以使用 use-package :vc 直接安装 使用 mysys2 的时候,系统会使用 unix 的 tar 去解压会有问题 windows 上面需要使用 system32/tar 这个程序去解压。

+begin_src emacs-lisp

解决办法可以是 mv /usr/bin/tar /usr/bin/tarbak

+end_src

** 上 Emacs China! 上 Emacs China! 上 Emacs China! (重要的事情说 3 遍)

视频地址如下:

how to Profile performace 怎么度量启动性能

+begin_src emacs-lisp

(use-package benchmark-init :ensure t :demand t :config ;; To disable collection of benchmark data after init is done. (add-hook 'after-init-hook 'benchmark-init/deactivate))

+end_src

use-package 哪些操作可以优化性能 https://systemcrafters.net/emacs-from-scratch/cut-start-up-time-in-half/ 借鉴大佬的配置,作为框架 *** 社区配置 Spacemacs & DoomEmacs

*** 个人配置

** 小技巧:

+begin_src emacs-lisp

(defun spacemacs/alternate-buffer (&optional window) "Switch back and forth between current and last buffer in the current window. If spacemacs-layouts-restrict-spc-tab' ist' then this only switches between the current layouts buffers." (interactive) (cl-destructuring-bind (buf start pos) (if (bound-and-true-p spacemacs-layouts-restrict-spc-tab) (let ((buffer-list (persp-buffer-list)) (my-buffer (window-buffer window))) ;; find buffer of the same persp in window (seq-find (lambda (it) ;; predicate (and (not (eq (car it) my-buffer)) (member (car it) buffer-list))) (window-prev-buffers) ;; default if found none (list nil nil nil))) (or (cl-find (window-buffer window) (window-prev-buffers) :key #'car :test-not #'eq) (list (other-buffer) nil nil))) (if (not buf) (message "Last buffer not found.") (set-window-buffer-start-and-point window buf start pos))))

+end_src

按键绑定:

+begin_src emacs-lisp

"TAB" 'spacemacs/alternate-buffer

+end_src