基于emacs打造自己的编辑器

emacs 学习

[TOC]

0 准备工作

0.1 首先修改镜像源

修改.emacs.d/init.el文件,在文件开始添加如下内容

(require 'package)
(add-to-list 'package-archives
         '("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/") t)

(setq package-archives '(("melpa-stable" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/stable-melpa/")
                        ("gnu" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/")
                        ("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/")
                        ("org" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/org/")))

0.2 帮助命令

1 where-is 命令 C-h w 函数名称

2 describe-key 命令 C-h k查看快捷键绑定的函数

3 describe-function 命令 C-h f查看函数的详细信息

4 describe-variable 查看变量的命令

5 describe-mode 查看当前mode的详细信息和快捷键

6 M-x decribe-face :: 查看某个mode的文档

7 describe-bindings 命令 列出当前mode组合键开头的快捷键

8 view-lossage :: 查看最近100个按键组合

9 info–emacs :: emacs的详细信息

1 编辑器

1.1 标签页

emacs(27.1之后)内置了两种类似的mode,tab-line-modetab-bar-mode,两种minor mode都可以实现。
!tab bar参考地址
!tab line参考地址
tab-bar-mode更简单一些,先用这个:

  • 打开/关闭模式
M-x tab-line-mode
  • 切换window(buffer)
C-x LEFT (previous-buffer)
C-x RIGHT (next-buffer)
  • 默认打开
    修改.emacs.d/init.el文件,在文件开始添加如下内容
(global-tab-line-mode t)

1.2 行号

  • 显示/不显示行号
'''linum-mode显示太丑了
M-x display-line-numbers-mode
  • 默认打开
    修改.emacs.d/init.el文件,在文件开始添加如下内容
(global-display-line-numbers-mode t)

1.3 目录树

使用插件sr-speedbar实现目录树,目录树在window中显示,嵌在frame中

  • 安装
M-x package-install RET sr-speedbar
  • 相关命令
sr-speedbar-open    Open sr-speedbar window.
sr-speedbar-close   Close sr-speedbar window.
sr-speedbar-toggle  Toggle sr-speedbar window.
sr-speedbar-select-window   select sr-speedbar window.
sr-speedbar-refresh-turn-on Turn on refresh speedbar content.
sr-speedbar-refresh-turn-off    Turn off refresh speedbar content.
sr-speedbar-refresh-toggle  Toggle refresh speedbar content.
  • 相关配置
(require 'sr-speedbar)
(setq speedbar-show-unknown-files t) 
(setq speedbar-use-images nil) 
(setq speedbar-tag-hierarchy-method nil)
(setq sr-speedbar-right-side nil)
(setq sr-speedbar-width 25)
(setq dframe-update-speed t)
(global-set-key (kbd "<f5>") (lambda()
          (interactive)
          (sr-speedbar-toggle)))

1.4 markdown

参考文档1
参考文档2

  • 安装
M-x package-install RET markdown-mode RET
M-x package-install RET markdown-preview-mode RET
  • 链接
C-c C-s l   [文本](url)
C-c C-s L   [文本](label)
C-c C-s u   直接插入链接
  • 风格
C-c C-s e   斜体
C-c C-s s   粗体
C-c C-s b   缩进
C-c C-s p   跳格
  • 标题
C-c C-t [1-6]   1-6级标题
  • 查看效果(无图形界面不支持)
C-c C-c m   显示对应的html
C-c C-c p   显示对应的html并显示效果(需配置)
  • 相关设置
(setq markdown-command "/usr/bin/pandoc")

1.5 yaml支持

  • 安装
M-x package-install RET yaml-mode RET
  • 相关设置
(require 'yaml-mode)
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))

1.6 pdf支持

  • 安装(stable)
M-x package-install RET pdf-tools RET
  • 相关设置
(require 'pdf-tools)
(pdf-tools-install)
(add-hook 'pdf-view-mode-hook 'pdf-view-fit-page-to-window)
(setq TeX-PDF-mode t)
(setq TeX-source-correlate-mode t) 
(setq TeX-source-correlate-method 'synctex) 
(setq TeX-source-correlate-start-server t)

(setq TeX-view-program-selection '((output-pdf "PDF Tools")))
`(add-hook 'TeX-after-compilation-finished-functions
          #'TeX-revert-document-buffer) 
  • 展示/关闭目录大纲
o/p
  • 跳转到上/下一个位置
l/r
  • 放大/大小
+/-

1.7 记笔记

  • 安装
M-x package-install RET org-noter RET
M-x package-install RET org-download RET
  • 相关设置
(require 'org-noter)
;;; 笔记查找路径
(setq org-noter-notes-search-path '("/sss" "/ss/ss" "jjjjj" ))
;;; 记录上一次笔记位置
(setq org-noter-auto-save-last-location t)
;;; 选中自动高亮
(setq org-noter-highlight-selected-text t)
;;; 长文本/短文本
(setq org-noter-max-short-selected-text-length 20) ;;;默认80
;;; 默认笔记标题
(setq org-noter-default-heading-title "第 $p$ 页的笔记")

(global-set-key (kbd "C-c n n") 'org-noter) ;;;打开org-noter
(define-key org-noter-doc-mode-map (kbd "e") 'org-noter-insert-note) ;;;插入页笔记
(define-key org-noter-doc-mode-map (kbd "M-e") 'org-noter-insert-precise-note) ;;;插入精确笔记
  • 用法
    打开
# pdf-tools 模式下打开会询问存储位置
M-x org-noter
# 也可直接打开org文件
C-x C-f

创建标注

M-x org-noter-create-skeleton 
# 会询问是否引入大纲,选择import

操作标注

# 打开标注
<tab>
# 打开/折叠标注
S-<tab>
# 所有标注升降级
M-S-<Left>
M-S-<Right>

插入笔记(在odf中)

# 在当前页插入笔记
i
# 在鼠标指定位置插入笔记
M-i
# 选中文字之后插入笔记
<tab>/M-i
# 高亮选中文字之后插入笔记
C-u <tab>

插入截图

# 先用系统截图工具完成截图
C-S-y
M-x org-download-clipboard

在原文中查找笔记

跳转到上一条/下一条笔记
C-M-n/p

1.8 Latex

  • 安装
M-x package-install RET cdlatex-mode RET
  • 相关设置
  • 用法
    编辑公式
# 手动补全$ 
$ a^2 + b^2 = c^2
# 自动展开
equ-<tab>

编译公式

C-c C-x C-l

1.9 补全

  • 说明

使用company-modecompany-mode是Emacs的文本补全框架。这个名字代表“完成任何事情”。它使用可插拔的后端和前端来检索和显示完成候选。它附带几个后端支持,如Elisp、Clang、Semantic、Eclim、Ropemacs、Ispell、CMake、BBDB、Yasnippet、dabbrev、etags、gtags、文件、关键字等。

通过MELPA安装:

M-x package-install RET company 

在全局激活:

(require 'company)
(add-hook 'after-init-hook 'global-company-mode)
  • 用法:

补全功能会在键入几个字母后将自动触发。使用M-nM-p进行上下选择,<return>完成选择或<tab>完成公共部分。用C-sC-rC-o搜索完成项。按M-(数字)快速完成前10位候选人中的一位。候选项显示出来后,按<f1>显示所选候选项的文档,或按C-w查看其来源。并非所有后端都支持这一点。

变量company-backends指定company-mode模式用于为您检索完成候选项的后端列表。

1.10 分屏

横向分屏

  • 开启
;; 向右分屏
C-x 3
  • 调整
;;增加宽度
C-x }
;;减少宽度 
C-x { 

纵向分屏

  • 开启
;;向上分屏
C-x 2
  • 增加高度
C-x ^

关闭分屏

  • 只保留当前分屏
C-x 1
  • 关闭当前分屏
C-x 0

1.11 高亮标记

参考文档1
参考文档2

  • 安装
M-x package-install RET symbol-overlay RET
# M-x package-install RET highlight-symbol RET
  • 快捷键设置
    在 ~/.emacs.d/init.el中添加:
(require 'symbol-overlay)
(global-set-key (kbd "M-i") 'symbol-overlay-put)
(global-set-key (kbd "M-n") 'symbol-overlay-switch-forward)
(global-set-key (kbd "M-p") 'symbol-overlay-switch-backward)
(global-set-key (kbd "<f7>") 'symbol-overlay-mode)
(global-set-key (kbd "<f8>") 'symbol-overlay-remove-all)
  • 快捷键说明
"i" -> symbol-overlay-put                ; 高亮或取消高亮当前symbol
"n" -> symbol-overlay-jump-next          ; 跳转到下一个位置
"p" -> symbol-overlay-jump-prev          ; 跳转到上一个位置
"w" -> symbol-overlay-save-symbol        ; 复制当前symbol
"t" -> symbol-overlay-toggle-in-scope    ; 切换高亮范围到作用域
"e" -> symbol-overlay-echo-mark          ; 撤销上一次跳转
"d" -> symbol-overlay-jump-to-definition ; 跳转到定义
"s" -> symbol-overlay-isearch-literally  ; 切换为isearch并搜索当前symbol
"q" -> symbol-overlay-query-replace      ; 查找替换当前symbol
"r" -> symbol-overlay-rename             ; 对symbol直接重命名

1.12 背景设置

  • 安装
M-x package-install RET color-theme-modern RET
  • 相关设置
(require 'color-theme-modern)
(load-theme 'deeper-blue)

;;set transparent effect
(global-set-key [(f11)] 'loop-alpha)
(setq alpha-list '((100 100) (95 65) (85 55) (75 45) (65 35)))
(defun loop-alpha ()
  (interactive)
  (let ((h (car alpha-list)))                ;; head value will set to
    ((lambda (a ab)
       (set-frame-parameter (selected-frame) 'alpha (list a ab))
       (add-to-list 'default-frame-alist (cons 'alpha (list a ab)))
       ) (car h) (car (cdr h)))
    (setq alpha-list (cdr (append alpha-list (list h))))
    )
)

1.13 日程管理

参考1
参考2

  • 安装
M-x package-install RET org-agenda RET
  • 相关设置
(setq org-agenda-files '("~/agenda/agenda.org"))

(setq org-todo-keywords
      '((sequence "TODO(t!)" "DOING(i!)" "BLOCKING(b!)" "|" "DONE(d!)" "CANCELLED(a@/!)")))

(setq org-todo-keyword-faces
   '(("TODO" .   (:foreground "red" :weight bold))
    ("BLOCKING" .   (:foreground "red" :weight bold))
    ("DOING" .      (:foreground "orange" :weight bold))
    ("DONE" .      (:foreground "green" :weight bold))
    ("CANCELLED" .     (:background "gray" :foreground "black"))
))

(global-set-key (kbd "C-c a") 'org-agenda)
  • 快捷键说明
# 标记状态
C-c C-t
# 安排时间
C-c C-s
# 打开agenda日程视图
C-c a
# 开始计时
C-c C-x C-i
# 结束/暂停计时
C-c C-x C-o
# 周时间统计报告,在agenda日程视图
v r
# 当日时间
v l

2 IDE

2.1 直接使用最流行的配置方案

参考文档

  • 更新插件列表
M-x package-list-package
  • 下载
git clone https://github.com/tuhdo/emacs-c-ide-demo.git ~/.emacs.d
  • 配置使用ggtags或者helm-gtags
    在~/.emacs.d/init.el中备注配置
(require 'setup-helm-gtags)
;; (require 'setup-ggtags)
  • 生效配置
M-x load-file RET init.el. 

2.2 在自己的编辑器上改造

2.2.1 代码目录

参考1.3

2.2.2 代码阅读

2.2.2.1 安装插件

可以选择ggtagshelm-gtags,两者都是基于gtags的,helm-gtags更高级更复杂一些。先从简单的ggtags入手。

  • 随编辑器启动,并绑定到附模式
(require 'ggtags)
(add-hook 'c-mode-common-hook
          (lambda ()
            (when (derived-mode-p 'c-mode 'c++-mode 'java-mode 'asm-mode)
              (ggtags-mode 1))))
  • 绑定快捷键,后面会描述
(define-key ggtags-mode-map (kbd "C-c g s") 'ggtags-find-other-symbol)
(define-key ggtags-mode-map (kbd "C-c g h") 'ggtags-view-tag-history)
(define-key ggtags-mode-map (kbd "C-c g r") 'ggtags-find-reference)
(define-key ggtags-mode-map (kbd "C-c g f") 'ggtags-find-file)
(define-key ggtags-mode-map (kbd "C-c g c") 'ggtags-create-tags)
(define-key ggtags-mode-map (kbd "C-c g u") 'ggtags-update-tags)

(define-key ggtags-mode-map (kbd "M-,") 'pop-tag-mark)
  • 生成GTAGS数据库

在主机上安装gtags

sudo yum install -y global

给工程生成数据库

# 切到工程根目录
cd ~/vpp;
gtags
ls G*
GPATH   GRTAGS  GTAGS

tag是源代码中实体的名称。一个实体可以是一个变量、一个方法定义、一个include运算符……一个标记包含诸如标记的名称(变量的名称、类、方法)、源代码中该标记的位置及其所属的文件等信息。

  • GTAGS: 实体定义库
  • GRTAGS: 实体引用库
  • GPATH: 路径名称库
2.2.2.2 在当前文件中查找定义

Imenu工具提供了一种在文件中按名称查找主要定义的方法,如函数定义、变量定义等。ggtags可以集成Imenu:

(setq-local imenu-create-index-function #'ggtags-build-imenu-index)

TODO

2.2.2.3 在工程中查找定义

默认的,在 ggtags-mode模式下M-. 运行ggtags-find-tag-dwim . 而命令ggtags-find-tag-dwim会根据上下文跳转到对应的tag:

  • 如果当前tag是定义,ggtags将跳转到引用。如果有多个引用,则显示引用列表。
  • 如果当前tag是定义,ggtags将跳转到定义。
  • 如果当前tag是头文件引用,则会跳转到该头文件

可以通过M-,触发pop-tag-mark跳回原来的位置(在那里调用了ggtags-find-tag-dwim)。

还可以在在空白处调用M-.时查找任意tag定义。在空白处。提示会要求您输入tag匹配,这里支持正则表达式。

如果ggtags给出一个候选列表,可以使用M-n移动到下一个候选,使用M-p移动回上一个候选。使用M-g s在候选缓冲区列表上调用Isearch

2.2.2.4 在工程中查找引用

运行ggtags-find-tag-dwim(通过快捷键M-.触发),跳转到定义或ggtags-find-reference(通过快捷键C-c g r触发),后者只查找引用。

ggtags-find-reference会给出一个候选列表,可以使用M-n移动到下一个候选,使用M-p移动回上一个候选,buffer内容会随之变化,非常好用。

2.2.2.5 在工程中查找文件

运行ggtags-find-file(通过快捷键C-c g f触发),需要输入文件名或关键字,也可以查找标识符。

2.2.2.6 查看浏览过的tag

一种是参考2.2.2.3,通过M-,触发pop-tag-mark,向前跳转;一种是通过C-c g h触发ggtags-view-tag-history,会列出所有访问过的tag,包括文件和tag。可以通过M-nM-p上下移动进行选择。

2.2.3 代码补全

2.2.3.1 记忆补全

参考1.5

2.2.3.2 源码补全

使用Clang

TODO

2.2.4 源码统计

TODO

3 emacs 定制和扩展

参考文档

emacs提供一部分默认配置,一些很好的特性是被警用或者被影藏的,比如:ibuffer、语义分析等。可能为了对新用户来说更加友好让他看起来想一个普通的编辑器,比如普通用户不需要自动匹配括号、花括号这些符号。

Emacs天生是一个可扩展的系统,大家编写插件来提高Emacs并分享给别人。这些插件提高了Emacs的各个方面:提供已有的和新出现的一些编辑特性,集成第三方工具,增加第三方编程语言支持,改善Emacs外观等等。如果没有扩展能力,Emacs仅仅只是另一个能提供几个有用特性的有马马虎虎的编辑器而已,但无法满足人们的需求,因为不同的人有不同的需求,而Emacs维护者无法将这些需求全部集成到Emacs中。但是通过扩展能力,人们可以将Emacs改造成他们自己想要的样子,非常类似于Lisp。

不同于其他编辑器鼓励用户尽量保持默认配置,Emacs鼓励用户尽量自己定制和扩展Emacs。

过去有一段时间,Emacs没有包管理工具,并且没有这么多学习资源,要定制Emacs非常不容易,因为定制Emacs可能需要你阅读大量的Emacs Lisp手册。大部分没有那么多空闲时间。我学习定制Emacs的办法是,当我遇到一些问题的时候,拷贝少量代码来解决这些特定问题,然后将它们都粘贴到我的~/.emacs文件中。我也必须手动下载插件包,然后在~/.emacs中手动加载它们。而现在有了包管理工具,就可以不用花那么多时间去收集配置,阅读大量Emacs lisp手册,也能比较轻松的定制自己需要的Emacs

3.1 Lisp基本语法

3.1.1 表达式

被执行的代码是一对括号和括号里的表达式:(...)。第一个元素肯定会被计算执行,Lisp环境(这里就是Emacs)会判断第一个标识符是3种表达式的哪一个:

  • 函数表达式:如果第一个元素指向一个函数定义,那么接下来的元素将会按照从左到右的顺序,作为参数传到函数中。比如:

    (message "Hello word %d" (+ 10 10))
    

    message是一个能够在打印区域messagebuffer中打印消息的函数。 Emacs检测到message是一个函数,然后依次计算字符串"Hello world %d"和另一个被括起来的表达式(+ 10 10)。当Emacs进入(+ 10 10),就会计算这个表达式,并返回20。然后字符串"Hello world %d"和数字20被传入message,最后在打印区域messagebuffer输出"Hello world 20"。

  • 特殊表达式:如果第一个元素是一个有特殊规则或者语法的Emacs Lisp预定义的函数,那剩下来的元素将根据预定义函数的规则进行评估。特殊表达式就是函数表达式,只是按照它自己的方式处理剩余的参数而不是按照函数默认的方式。所以称之为特殊表达式。比如:

    (if condition
        true-case
        false-case)
    

    condition是一个合法的Lisp表达式;如果判断为真(任意不为NIL或空表的值),那么,合法的Lisp表达式true-case被执行;否则 ,合法的Lisp表达式false-case被执行。
    另一种特殊表达式是and。Emacs判断第一个元素是一个and表达式后,会从左到右依次计算后续的元素,直到其中一个元素返回false。如果每个元素都被计算,and返回true.

  • 宏表达式:如果第一个元素是由函数defmacro创建的(在这个阶段您不需要知道),那么Emacs不会计算任何剩余元素,而是将它们作为数据传入。宏是程序员创建自己的迷你语言的一种方式,它不遵循Emacs中预定义的计算规则(函数表达式和特殊表达式)。如果您阅读的表达式不是函数表达式或任何预定义的特殊表达式,请不要惊慌!这是一种宏形式。程序员肯定会提供如何使用他们的迷你语言的文档。

总之,那些不同的表达式其实都是一种类型:函数表达式。函数表达式是一种通用表达式,特殊表达式中一种有自己规则的函数表达式;宏表达式是一种能生成代码的函数表达式。

3.1.2 数据:有两种形式的数据

  • 原子数据:原始类型的数据,比如:数字、字符串、标识符和NIL
  • 非原子数据:如果你在表达式之前添加一个',它就变成一个了列表(list)。Emacs将这些被引用的表达式当做数据处理
    Emacs支持其他类型的数据,比如数组、哈希表等,但列表(list)是最常用的,当对性能没有啥需求的时候,用他就够了。

3.2 常用函数

介绍一些常用函数,其他函数可以通过C-h fC-h v获取帮助文档学习。

3.2.1 函数:(setq [ SYM VAL ]...)

Comment: A really fundamental function for customizing Emacs settings. An Emacs setting is really just a variable. Emacs has GUI for changing setting, but setq a variable is also equivalent.

  • 备注
    在定制Emacs配置时,这是一个非常重要的函数。一个Emacs配置就是一个变量。Emacs可以通过GUI进行配置,但是setq也是同等效力的。

  • 示例

(setq global-mark-ring-max 50000)
  • 文档

给每个SYM赋值。标识符SYM是变量,不会被计算。值VAL是表达式,是会被计算的。所以(setq x (1+y))是将1+y的值赋给x。直到第一个SYM被赋值之后,第二个VAL才会被计算,以此类推;每个VAL都可以使用前期在setq-中赋值的变量值

3.2.2 函数:(load FILE &optional NOERROR NOMESSAGE NOSUFFIX MUST-SUFFIX)

  • 备注
    这个函数用来加载文件,FILE是在加载路径下能找到的文件名。&optional表示在其之后的所有内容都是可选的。如果不需要,NOERROR NOMESSAGE NOSUFFIX这些参数都可以不用传给load。然而,如果你想传递其中一个参数给函数,那么你必须将这个参数左边的所有参数都传过去。比如:你想传递NOSUFFIX,你必须首先传NOERROR, NOMESSAGE,而MUST-SUFFIX可以不用传递。

  • 示例

(load (substitute-in-file-name "$HOME/.emacs.d/module")) ;; first try to load module.elc; if not found, try to load module.el
(load (substitute-in-file-name "$HOME/.emacs.d/module.el")) ;; only load module.el
(load (substitute-in-file-name "$HOME/.emacs.d/module.elc")) ;; only load module.elc
(load "module") ; search for the file module.el or module.elc in variable load-path
  • 文档:
    执行一个名为FILE的Lisp代码文件。首先会尝试添加.elc后缀解析文件,然后会尝试添加.el后缀解析文件,然后尝试不添加后缀(精确的后缀顺序取决于load-suffixes)。通过调用substitute-in-file-name函数,可以替换FILE文件中关联的环境变量。这个函数会在load-path设置的路径中进行检索。ches the directories in `load-path'.
    (C-h f for more information)

3.2.3 函数:(require FEATURE &optional FILENAME NOERROR)

  • 备注
    如果是通过M-x list-packages安装的包,可以使用(require 'installed-package)加载这个包。

  • 示例

(require 'volatile-highlights)

加载已安装的volatile-highlights包。

  • 文档
If feature FEATURE is not loaded, load it from FILENAME. If FEATURE is not a member of the list `features', then the feature is not loaded; so load the file FILENAME. If FILENAME is omitted, the printname of FEATURE is used as the file name, and `load' will try to load this name appended with the suffix `.elc' or `.el', in that order. The name without appended suffix will not be used. See `get-load-suffixes' for the complete list of suffixes. If the optional third argument NOERROR is non-nil, then return nil if the file is not found instead of signaling an error. Normally the return value is FEATURE. The normal messages at start and end of loading FILENAME are suppressed.

3.2.4 函数:(provide FEATURE &optional SUBFEATURES)

  • 备注
    你可以在一个文件末尾使用这个函数,从而将文件转换为可加载模块,该模块仅在使用require调用时加载。

  • 示例
    假设在文件设置编辑中。你把这行代码放在最下面:

(provide 'setup-editing)

然后,即使您使用加载功能加载它,它也不会被激活。要激活,您必须执行(provide 'setup-editing)

  • 文档
Announce that FEATURE is a feature of the current Emacs. The optional argument SUBFEATURES should be a list of symbols listing particular subfeatures supported in this version of FEATURE.

3.2.5 函数:(add-to-list LIST-VAR ELEMENT &optional APPEND COMPARE-FN)

  • 备注
    将元素 ELEMENT 添加到名为 LIST-VAR 的list中去。

  • 示例

(add-to-list 'load-path "~/.emacs.d/personal") ; add personal to load-path,
                                               ; so "load" function can search for files in it
  • 文档
Add ELEMENT to the value of LIST-VAR if it isn't there yet. The test for presence of ELEMENT is done with `equal', or with COMPARE-FN if that's non-nil. If ELEMENT is added, it is added at the beginning of the list, unless the optional argument APPEND is non-nil, in which case ELEMENT is added at the end.

The return value is the new value of LIST-VAR.

This is handy to add some elements to configuration variables, but please do not abuse it in Elisp code, where you are usually better off using `push' or `cl-pushnew'.

If you want to use `add-to-list' on a variable that is not defined until a certain package is loaded, you should put the call to `add-to-list' into a hook function that will be run only after loading the package. `eval-after-load' provides one way to do this. In some cases other hooks, such as major mode hooks, can do the job.

3.2.6 函数:(add-hook HOOK FUNCTION &optional APPEND LOCAL)

  • 备注
    hook是一个关联一系列函数的Lisp变量,这些函数会在预定义的场景下被调用。这就是运行这个hook,可以执行C-h v输入-hook后缀然后TAB来查找各种hook。

  • 示例

(add-hook 'prog-mode-hook 'linum-mode)

添加linum-mode函数之后,每次你进入一个prog-mode(所有编程主模式都会继承),都会触发在emacs左侧显示行号。编程模式包括c-mode、asm-mode、java-mode等

  • 文档
Add to the value of HOOK the function FUNCTION. FUNCTION is not added if already present. FUNCTION is added (if necessary) at the beginning of the hook list unless the optional argument APPEND is non-nil, in which case FUNCTION is added at the end.

The optional fourth argument, LOCAL, if non-nil, says to modify the hook's buffer-local value rather than its global value. This makes the hook buffer-local, and it makes t a member of the buffer-local value. That acts as a flag to run the hook functions of the global value as well as in the local value.

HOOK should be a symbol, and FUNCTION may be any valid function. If HOOK is void, it is first set to nil. If HOOK's value is a single function, it is changed to a list of functions.

3.2.7 函数:(global-set-key KEY COMMAND)

  • 备注
    如函数格式所展示的,该函数将命令绑定到键。

  • 示例
    可以通过以下几种方式进行绑定:

(global-set-key (kbd "C-x C-b") 'ibuffer) ;; 将“C-x C-b”绑定到ibuffer命令
(global-set-key "\C-x\C-b" 'ibuffer)  ;; 将“C-x C-b”绑定到ibuffer命令,但修改键必须用反斜杠转义 
(global-set-key [?\C-x?\C-b] 'ibuffer) ;; 使用vector而不是字符串

推荐使用(kbd…)函数,因为我们可以使用我们熟悉的键符号编写键绑定,而无需添加不必要的字符。Vector在其他语言中就是数组,用于映射功能键,如[left]、[right]、[up]、[down]、[f1]…[f12]。但现在,您还可以在(kbd…)函数中使用尖括号(<>)映射功能键:

(global-set-key (kbd "<f3>") 'kmacro-start-macro-or-insert-counter)

以下是常用的功能键(请记住用一对尖括号将它们括起来):

按键 描述
left, up, right, down 光标箭头
begin, end, home, next, prior 其他光标重新定位键 prior=PageUp,next=PageDown
select, print, execute, backtab 其他键 backtab=S-TAB,C-iso-tab
f1, f2, ... F35 键盘顶部的编号功能键
kp-add, kp-subtract, kp-multiply, kp-divide 带名称或标点符号的键盘键(在普通键盘的右侧)
begin, end, home, next, prior 其他光标重新定位键 prior=PageUp,next=PageDown
  • 文档
Give KEY a global binding as COMMAND. COMMAND is the command definition to use; usually it is a symbol naming an interactively-callable function. KEY is a key sequence; noninteractively, it is a string or vector of characters or event types, and non-ASCII characters with codes above 127 (such as ISO Latin-1) can be included if you use a vector.

Note that if KEY has a local binding in the current buffer, that local binding will continue to shadow any global binding that you make with this function.

3.2.8 函数:(define-key KEYMAP KEY DEF)

  • 备注
    此函数将定义DEF(通常是命令)绑定到键序列KEY。定义可以是您可以在内置文档中找到的其他内容。
    key序列(简称key)是一系列输入事件,它们作为一个单元具有意义。输入事件包括字符、功能键和鼠标按钮——所有可以发送到计算机的输入。键序列从其绑定中获得其含义,该绑定表示它运行的命令。
    当按下键序列key时,Emacs运行相关功能。键映射keymap存储KEY和定义DEF之间的绑定列表。主模式或次模式使用键映射来提供自己的键绑定。键映射通常有-mode-map后缀,如:dired-mode-map;如果要在主模式或次模式下更改或添加键绑定,请使用如下定义键函数:

  • 示例

;; Dired uses "e", "f" or RET to open a file
;; you can reuse one of these keys for different purpose
;; for example, you can bind it to wdired-change-to-wdired-mode
;; wdired-change-to-wdired-mode allows you to edit your Dired buffer
;; like a normal text buffer, such as edit file/directory names,
;; permission bits.. and then commit the changes to disk.
;;
;; "e" is short for "edit"
;; After finish your editing, "C-c C-c" to commit, "C-c C-k" to abort
(define-key dired-mode-map (kbd "e") 'wdired-change-to-wdired-mode)
  • 文档
In KEYMAP, define key sequence KEY as DEF. KEYMAP is a keymap.

KEY is a string or a vector of symbols and characters, representing a sequence of keystrokes and events. Non-ASCII characters with codes above 127 (such as ISO Latin-1) can be represented by vectors. Two types of vector have special meanings: [remap COMMAND] remaps any key binding for COMMAND. [t] creates a default definition, which applies to any event with no other definition in KEYMAP.

DEF is anything that can be a key's definition: nil (means key is undefined in this keymap), a command (a Lisp function suitable for interactive calling), a string (treated as a keyboard macro), a keymap (to define a prefix key), a symbol (when the key is looked up, the symbol will stand for its function definition, which should at that time be one of the above, or another symbol whose function definition is used, etc.), a cons (STRING . DEFN), meaning that DEFN is the definition (DEFN should be a valid definition in its own right), or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP, or an extended menu item definition. (See info node `(elisp)Extended Menu Items'.)

If KEYMAP is a sparse keymap with a binding for KEY, the existing binding is altered. If there is no binding for KEY, the new pair binding KEY to DEF is added at the front of KEYMAP.

3.2.9 函数:(defalias SYMBOL DEFINITION &optional DOCSTRING)

  • 备注
    defalias允许您重命名命令。它通常用于缩写命令名。

  • 示例
    在init.el中添加以下别名

(defalias 'yes-or-no-p 'y-or-n-p) ; y or n is enough
(defalias 'list-buffers 'ibuffer) ; always use ibuffer

                                        ; elisp
(defalias 'eb 'eval-buffer)
(defalias 'er 'eval-region)
(defalias 'ed 'eval-defun)

                                        ; minor modes
(defalias 'wsm 'whitespace-mode)
  • 文档
Set SYMBOL's function definition to DEFINITION. Associates the function with the current load file, if any. The optional third argument DOCSTRING specifies the documentation string for SYMBOL; if it is omitted or nil, SYMBOL uses the documentation string determined by DEFINITION.

Internally, this normally uses `fset', but if SYMBOL has a `defalias-fset-function' property, the associated value is used instead.

The return value is undefined.

3.2.10 函数:(mapc FUNCTION SEQUENCE)

  • 备注
    mapc对序列SEQUENCE中的每个元素调用函数FUNCTION

  • 示例

;; load every .el file inside ~/.emacs.d/custom/
(mapc 'load (directory-files "~/.emacs.d/custom" t ".*\.el"))
  • 文档
Apply FUNCTION to each element of SEQUENCE for side effects only. Unlike `mapcar', don't accumulate the results. Return SEQUENCE. SEQUENCE may be a list, a vector, a bool-vector, or a string.

3.2.11 宏:(defun NAME ARGLIST &optional DOCSTRING DECL &rest BODY)

  • 备注
    defun是一个Lisp宏,它允许您像任何其他语言一样定义函数。函数是要执行的Lisp表达式的集合。返回值是最后一个表达式

  • 示例
    创建一个普通函数 (在 M-x不可用):

(defun demo ()
  (message "Hello World" number string))

创建一个命令 (在 M-x可用):

(defun demo ()
  (interactive)
  (message "Hello World"))

interactive是一种特殊的表达式,它将函数转换为命令,并允许命令接受各种类型的前缀参数,例如数字、字符串、符号、缓冲区名称…您可以使用C-h f键入interactive来了解更多信息。

  • 文档
Define NAME as a function. The definition is (lambda ARGLIST [/DOCSTRING/] BODY…). See also the function `interactive'. DECL is a declaration, optional, of the form (declare DECLS…) where /DECLS is a list of elements of the form (PROP . VALUES). These are interpreted according to `defun-declarations-alist'. The return value is undefined.

3.3 常用内置快捷键

3.3.1 C-M-f

forward-sexp绑定,向前移动到一个对应的表达式。

3.3.2 C-M-b

backward-sex绑定,向后移动到一个对应的表达式。

3.3.3 C-M-k

kill-sexp绑定,向前删除表达式内全部内容。

3.3.4 C-M-t

transpose-sex绑定,转置表达式。

3.3.5 C-M-<SPC> or C-M-@

mark-sexp绑定,标记整个表达式内的内容。

3.4 Emacs包管理工具

TODO

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容