第 25 章 Emacs 入门
目录
基础知识
引起我们痛苦的东西我们就会去爱它,以使自己觉得这份痛苦是值得的
Emacs 是一个架构在编辑器上的集成环境,除了最基本的编辑功能,还可以完成文件管理、终端模拟、浏览网页、收发邮件、编译程序等工作。
Emacs 使用 Elisp 语言进行配置和扩展,它本身也可以作为 Elisp 解释器使用。
Emacs 的界面主要由三部分构成:信息栏、回显区(echo)、缓冲区(buffer)
缓冲区
缓冲区(buffer) 类似于常规编辑器的文字编辑区。Emacs 并不直接对文件进行修改,而是读取文件的内容并显示在缓冲区中,在收到保存的指令后才将修改写入文件。
缓冲区名称通常为它所读取文件的文件名。
信息栏
在缓冲区之下为状态栏,像这样的
简单说明一下:
-U(UNIX)**- emacs.xml 15% (28,32) Git:master (DocBook-XML Outl)----二 9月 30 11:57--------------------
标题栏也可以显示一部分信息,并且可以自由定义。
按键描述
Emacs 的功能键,通常为组合键。例如
表 25.1. Emacs 按键描述
Emacs 描述 | 实际热键 | 功能 |
---|---|---|
C-f | Ctrl+f | 光标前进一格 |
C-b | Ctrl+b | 光标后退一格 |
C-d | Ctrl+d | 删除一个字符 |
C-a | Ctrl+a | 回到行首 |
Emacs 对按键的描述方式中, - 之前的一个字符为修饰键,表示按住该键,再按 - 后面的键。
例如: C-a
表示按住 Ctrl 再按 a 键。 Emacs 对其它一些特殊按键的描述
C- |
按住 Ctrl键 |
M- |
按住 Meta键。在 PC 上,Meta键 通常对应 Alt 键 |
C-M- |
同时按住 Ctrl键 和 Meta键 |
S- |
Shift键 |
s- |
Linux 下对应 WIN 键 |
RET |
回车键 |
TAB |
Tab键 |
ESC |
Esc键 |
SPC |
空格键 |
DEL |
退格键 |
Delete |
删除键 |
注意:在 Emacs 中,
ESC
等价于M-
,也就是说,按一次ESC
键,与按住Meta
键的作用是相同的。类似
C-M-r
的按键,可以使用ESC C-r
(按一次ESC
,再按C-r
)这种更容易的方式
在后面的部分中,将统一使用 Emacs 对按键的描述方式。
Emacs 十分强大,上面的组合键,远不能涵盖 Emacs 的功能于万一。除基本的编辑功能键外,其它功能多使用 按键序列: 连续的按下多组快捷键
例如: C-x C-c
表示先按下 C-x
,再按下 C-c
。也就是 Ctrl+x
后,再 Ctrl+c
(退出 Emacs)
接下来 C-h t
,进入 《Emacs 快捷指南》
回显区
C-x h
(先按 Ctrl+x 再按 h) 后,您会发现状态栏和编辑器底部之间的区域出现 Mark set 字样。同时,整个缓冲区的内容都被选中。
它是一个迷你缓冲区(minibuffer),叫作回显区(echo area),提示您正在进行的操作,比如 Mark set(设定标记)
如果一个按键序列没有完成,却停止了输入。大约两秒后,回显区会显示已输入部分,以免您忘记。不要以为是 Emacs 反应迟钝
内部命令
C-h k
后,回显区提示
Describe key (or click or menu item):
接着 C-x h
,您会发现,缓冲区被水平分割为两个。另一个名为 help 缓冲区中显示的内容为
C-x h runs the command mark-whole-buffer
which is an interactive compiled Lisp function in `simple.el'.
It is bound to C-x h, <menu-bar> <edit> <mark-whole-buffer>.
(mark-whole-buffer)
Put point at beginning and mark at end of buffer.
You probably should not use this function in Lisp programs;
it is usually a mistake for a Lisp function to use any subroutine
that uses or sets the mark.
[back]
第一行说明了 C-x h
运行的命令为 mark-whole-buffer
第二行说明了该命令由 simple.el
这个扩展提供,绑定到 C-x h
、 菜单栏-编辑-标记全部缓冲区 、 和命令 mark-whole-buffer
第三行介绍了这个命令的行为: 在文档末尾设置一个标记,并把光标点[45]移动到文档起始。
注意:Emacs 使用命令进行处理,快捷键只是一种发送命令的方法!
一般情况下,我们用不到这么详细的说明,而且英文看起来也比较吃力。您可以使用 C-h c
以简洁模式查看说明。 只在回显区显示键位和它执行的命令:
C-x h runs the command mark-whole-buffer
通常这就足够了。
如果您知道一个命令,而不知道它绑定到什么键上,您可以使用 C-h w
,也就是命令 Where-is
Emacs 命令行
由于 Emacs 太过强大,内部命令恒河沙数,根本不可能有同样数量的快捷键位来绑定它们!
对于没有绑定的命令,可以使用命令执行!
M-x
(Alt+x) 开启命令行,回显区显示为 M-x ,然后输入 newline
这个命令默认绑定在回车键,所以它和回车键的作用一样为 换行
C-h w newline 结果是: newline is on RET
提示:命令行中,可以使用
TAB
补全,使用M-p
上翻,M-n
下翻
在后面的部分中,统一使用 M-x command
来表示 内部命令 command ;内部命令以 "(command)" 的形式写到配置文件中
Emacs 终端
M-x shell
激活 Emacs 终端。可以在 Emacs 终端中使用外部命令。
需要注意的是,Emacs 终端是哑终端,某些类型的输出不能够正确显示。
在 Emacs 终端中使用 exit
命令退出。
M-!
(Alt+Shift+1)临时执行一条外部命令,并输出在名为 Shell Command Output 的缓冲区中 (M-x shell-command)
C-u M-!
(Ctrl+u Alt+Shift+1)临时执行一条外部命令,并输出到光标位置。
Emacs 文件管理器
C-x d
进入 Dired 列表模式
C-x C-d
获取文件列表(简洁)
C-x C-f
打开文件,输入路径为打开目录
详细介绍见 “文件管理”一节
区块选择
很多时候,我们需要选中缓冲区中的某一部分内容。和大多数程序一样,您可以在被选择区块的起始点按下左键,移动鼠标,在结束点释放左键,这部分区块便被选中。
这种方式效率并不高,而且一些场合并没有鼠标支持,例如控制台或者远程登录。
事实上,Emacs 进行区块选择的方法,是设置一个标记,标记到光标点[45]之间的部分将被选中。
标记的位置为 M-x set-mark-command 时,光标点[45]所处的位置。
M-x set-mark-command 是设置标记的内部命令,默认绑定在 C-SPC
键上。
如果使用输入法,这个键位多半是切换输入法的快捷键。键盘指令会先被输入法拦截下来,而无法发送到 Emacs。
当然也可以使用 M-@
来设定标记。不过 M-@
原绑定为 M-x mark-word ,虽然差不太多,但有时并不好用;况且对于一个常用的命令来讲, M-@
键位的难度太高了
Emacs 的键位中,几乎没有默认绑定在 WIN 键上的命令,不妨利用一下
在 Emacs 的用户配置文件 ~/.emacs
中添加如下内容:
;; WIN+Space 设置标记
(global-set-key (kbd "s-SPC") 'set-mark-command)
在某些类型的终端中,WIN键 不起作用,建议使用命令。或者绑定到 C-t
[46]
;; (在注释里说明原命令和绑定,是一个良好的习惯)
;; C-t 设置标记
(global-set-key (kbd "C-t") 'set-mark-command)
重要:重启 Emacs,或者在 ~/.emacs 文件的缓冲区中执行命令
M-x eval-buffer
,便可以使配置文件立即生效
中止执行
如果想放弃一个命令,可以使用 C-g
(M-x keyboard-quit)打断。
建议您使用快捷键 C-g ,因为在需要中止执行的情况下, M-x 通常是无法使用的
ESC ESC ESC
(M-x keyboard-escape-quit)可以从一些交互命令中退出。
例如从 "询问替换 M-x query-replace" 中退出。
当 C-g 不能搞定,您可以尝试连按三次 ESC
[45] 光标点假定光标为插入式(竖线),位置在覆盖式光标(方块)的左侧。
事实上,Emacs 中的相关判定以光标点为准!方块形光标只是为了减少视觉疲劳
[46] 这是一个让人头痛不已的地方。因为无论绑到哪,似乎都不太方便:
使用 WIN键 倒是挺好,但是在字符界面下,WIN键 通常不起作用;同样,C-; 这样使用标点的组合键在字符界面下也不行;
C-m、C-i 是两个不错的组合,但是 Emacs 认为 C-m 和 RET、C-i 和 TAB 是一个键,这样绑定,你的回车或者 TAB 就成了设置标记;
最后,C-t 这个键默认绑定的命令几乎没什么用,只是这个键位不是很好按,但这样也有好处──无论你用左手或者右手来按 t键,距离都差不多
基本配置
您已经知道了,Emacs 的配置文件为 ~/.emacs
。配置文件中,以 ; 起始到行末的部分为注释。
让我们些简单配置一下:
例 25.1. emacs 配置 ~/.emacs
;;========================================
;;添加 Emacs 搜索目录 可以将自定的扩展放该目录
;;========================================
;(add-to-list 'load-path "~/..emacs")
;如果有其它配置文件,使此命令读取
;(load "addon.el")
;;========================================
;; 外观设置
;;========================================
;;禁用工具栏
(tool-bar-mode nil)
;;禁用菜单栏,F10 开启关闭菜单
(menu-bar-mode nil)
;;禁用滚动栏,用鼠标滚轮代替
;;(scroll-bar-mode nil)
;;禁用启动画面
(setq inhibit-startup-message t)
;;========================================
;; 键绑定
;;========================================
;; C-t 设置标记 ;;
(global-set-key (kbd "C-t") 'set-mark-command)
;; C-x b => CRM bufer list
(global-set-key "\C-xb" 'electric-buffer-list)
;;---------- redo
(global-set-key ( kbd "C-.") 'redo)
;;========================================
;;关闭当前缓冲区 Alt+4 ;; C-x 0
(global-set-key (kbd "M-4") 'delete-window)
;;关闭其它缓冲区 Alt+1 ;; C-x 1
(global-set-key (kbd "M-1") 'delete-other-windows)
;;水平分割缓冲区 Alt+2 ;; C-x 2
(global-set-key (kbd "M-2") 'split-window-vertically)
;;垂直分割缓冲区 Alt+3 ;; C-x 3
(global-set-key (kbd "M-3") 'split-window-horizontally)
;;切换到其它缓冲区 Alt+0 ;; C-x o
(global-set-key (kbd "M-0") 'other-window)
;;F10 显示/隐藏菜单栏 ;; M-x menu-bar-open
;;(global-set-key (kbd "F10") 'menu-bar-mode)
;;WIN+s 进入 Shell ;; M-x shell
;(global-set-key (kbd "s-s") 'shell)
;;(define-key ctl-x-map "\M-s" 'shell)
;;========================================
;; 缓冲区
;;========================================
;;设定行距
(setq default-line-spacing 0)
;;页宽
(setq default-fill-column 90)
;;缺省模式 text-mode
(setq default-major-mode 'text-mode)
;;设置删除纪录
(setq kill-ring-max 200)
;;以空行结束
(setq require-final-newline t)
;;语法加亮
(global-font-lock-mode t)
;;高亮显示区域选择
(transient-mark-mode t)
;;页面平滑滚动, scroll-margin 5 靠近屏幕边沿3行时开始滚动,可以很好的看到上下文。
(setq scroll-margin 5
scroll-conservatively 10000)
;高亮显示成对括号,但不来回弹跳
(show-paren-mode t)
(setq show-paren-style 'parentheses)
;;鼠标指针规避光标
;(mouse-avoidance-mode 'animate)
;;粘贴于光标处,而不是鼠标指针处
(setq mouse-yank-at-point t)
;;========================================
;; 回显区
;;========================================
;;闪屏报警
(setq visible-bell t)
;;使用 y or n 提问
(fset 'yes-or-no-p 'y-or-n-p)
;;锁定行高
(setq resize-mini-windows nil)
;;递归 minibuffer
(setq enable-recursive-minibuffers t)
;; 当使用 M-x COMMAND 后,过 1 秒钟显示该 COMMAND 绑定的键。
;;(setq suggest-key-bindings 1) ;;
;;========================================
;; 状态栏
;;========================================
;;显示时间
(display-time)
;;时间格式
(setq display-time-24hr-format t)
(setq display-time-day-and-date t)
(setq display-time-interval 10)
;;显示列号
(setq column-number-mode t)
;;标题栏显示 %f 缓冲区完整路径 %p 页面百分数 %l 行号
(setq frame-title-format "%f")
;;========================================
;; 编辑器设定
;;========================================
;;不生成临时文件
;;(setq-default make-backup-files nil)
;;只渲染当前屏幕语法高亮,加快显示速度
(setq font-lock-maximum-decoration t)
;;将错误信息显示在回显区
;(condition-case err
; (progn
; (require 'xxx) )
; (error
; (message "Can't load xxx-mode %s" (cdr err))))
;;使用X剪贴板
(setq x-select-enable-clipboard t)
;;;;;;;; 使用空格缩进 ;;;;;;;;
;; indent-tabs-mode t 使用 TAB 作格式化字符 nil 使用空格作格式化字符
(setq indent-tabs-mode nil)
(setq tab-always-indent nil)
(setq tab-width 4)
;;========================================
;; 颜色设置
;;========================================
;; 指针颜色
(set-cursor-color "black")
;; 鼠标颜色
(set-mouse-color "black")
;; 背景和字体颜色
(set-foreground-color "gainsboro")
(set-background-color "grey30")
(set-border-color "black")
;; 语法高亮显示,区域选择,二次选择 ;;前景和背景色
(set-face-foreground 'highlight "white")
(set-face-background 'highlight "blue")
(set-face-foreground 'region "cyan")
(set-face-background 'region "blue")
(set-face-foreground 'secondary-selection "skyblue")
(set-face-background 'secondary-selection "darkblue")
;;日历配色
;(setq calendar-load-hook
;'(lambda ()
;(set-face-foreground 'diary-face "skyblue")
;(set-face-background 'holiday-face "slate blue")
;(set-face-foreground 'holiday-face "white")))
;;========================================
;; 字体设置
;;========================================
(set-default-font "Monospace-10")
(if window-system
(set-fontset-font (frame-parameter nil 'font)
'unicode '("Microsoft YaHei" . "unicode-bmp"))
)
;;========================================
;; 必备扩展
;;========================================
;;---------- color-theme
;(require 'color-theme)
;(color-theme-gray30)
;;========== ibuffer
(require 'ibuffer)
(global-set-key ( kbd "C-x C-b ")' ibuffer)
;;========== outline
(setq outline-minor-mode-prefix [(control o)])
;;---------- Docbook
;(require 'docbook-xml-mode)
;(add-hook 'docbook-xml-mode-hook
; (function (lambda ()
; (setq outline-regexp "<!\\-\\-\\*+")
; (outline-minor-mode)
; (hide-body)
; )))
;;开启服务器模式
;(server-start)
;;org-mode
(setq org-hide-leading-stars t)
(define-key global-map "\C-ca" 'org-agenda)
(setq org-log-done 'time)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;; F5 运行当前文件 ;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;(defun run-current-file ()
; "Execute or compile the current file.
;For example, if the current buffer is the file x.pl,
;then it'll call “perl x.pl” in a shell.
;The file can be php, perl, python, bash, java.
;File suffix is used to determine what program to run."
;(interactive)
; (let (ext-map file-name file-ext prog-name cmd-str)
;; get the file name
;; get the program name
;; run it
; (setq ext-map
; '(
; ("php" . "php")
; ("pl" . "perl")
; ("py" . "python")
; ("sh" . "bash")
; ("java" . "javac")
; )
; )
; (setq file-name (buffer-file-name))
; (setq file-ext (file-name-extension file-name))
; (setq prog-name (cdr (assoc file-ext ext-map)))
; (setq cmd-str (concat prog-name " " file-name))
;; (compile cmd-str)))
; (shell-command cmd-str)))
;
;(global-set-key (kbd "<f5>") 'run-current-file)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;; 以下为实现 redo 的代码 ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'redo)
(defvar redo-version "1.02"
"Version number for the Redo package.")
(defvar last-buffer-undo-list nil
"The head of buffer-undo-list at the last time an undo or redo was done.")
(make-variable-buffer-local 'last-buffer-undo-list)
(make-variable-buffer-local 'pending-undo-list)
;; Emacs 20 variable
(defvar undo-in-progress)
(defun redo (&optional count)
"Redo the the most recent undo.
Prefix arg COUNT means redo the COUNT most recent undos.
If you have modified the buffer since the last redo or undo,
then you cannot redo any undos before then."
(interactive "*p")
(if (eq buffer-undo-list t)
(error "No undo information in this buffer"))
(if (eq last-buffer-undo-list nil)
(error "No undos to redo"))
(or (eq last-buffer-undo-list buffer-undo-list)
;; skip one undo boundary and all point setting commands up
;; until the next undo boundary and try again.
(let ((p buffer-undo-list))
(and (null (car-safe p)) (setq p (cdr-safe p)))
(while (and p (integerp (car-safe p)))
(setq p (cdr-safe p)))
(eq last-buffer-undo-list p))
(error "Buffer modified since last undo/redo, cannot redo"))
(and (or (eq buffer-undo-list pending-undo-list)
(eq (cdr buffer-undo-list) pending-undo-list))
(error "No further undos to redo in this buffer"))
(or (eq (selected-window) (minibuffer-window))
(message "Redo..."))
(let ((modified (buffer-modified-p))
(undo-in-progress t)
(recent-save (recent-auto-save-p))
(old-undo-list buffer-undo-list)
(p (cdr buffer-undo-list))
(records-between 0))
;; count the number of undo records between the head of the
;; undo chain and the pointer to the next change. Note that
;; by `record' we mean clumps of change records, not the
;; boundary records. The number of records will always be a
;; multiple of 2, because an undo moves the pending pointer
;; forward one record and prepend a record to the head of the
;; chain. Thus the separation always increases by two. When
;; we decrease it we will decrease it by a multiple of 2
;; also.
(while p
(cond ((eq p pending-undo-list)
(setq p nil))
((null (car p))
(setq records-between (1+ records-between))
(setq p (cdr p)))
(t
(setq p (cdr p)))))
;; we're off by one if pending pointer is nil, because there
;; was no boundary record in front of it to count.
(and (null pending-undo-list)
(setq records-between (1+ records-between)))
;; don't allow the user to redo more undos than exist.
;; only half the records between the list head and the pending
;; pointer are undos that are a part of this command chain.
(setq count (min (/ records-between 2) count)
p (primitive-undo (1+ count) buffer-undo-list))
(if (eq p old-undo-list)
nil ;; nothing happened
;; set buffer-undo-list to the new undo list. if has been
;; shortened by `count' records.
(setq buffer-undo-list p)
;; primitive-undo returns a list without a leading undo
;; boundary. add one.
(undo-boundary)
;; now move the pending pointer backward in the undo list
;; to reflect the redo. sure would be nice if this list
;; were doubly linked, but no... so we have to run down the
;; list from the head and stop at the right place.
(let ((n (- records-between count)))
(setq p (cdr old-undo-list))
(while (and p (> n 0))
(if (null (car p))
(setq n (1- n)))
(setq p (cdr p)))
(setq pending-undo-list p)))
(and modified (not (buffer-modified-p))
(delete-auto-save-file-if-necessary recent-save))
(or (eq (selected-window) (minibuffer-window))
(message "Redo!"))
(setq last-buffer-undo-list buffer-undo-list)))
(defun undo (&optional arg)
"Undo some previous changes.
Repeat this command to undo more changes.
A numeric argument serves as a repeat count."
(interactive "*p")
(let ((modified (buffer-modified-p))
(recent-save (recent-auto-save-p)))
(or (eq (selected-window) (minibuffer-window))
(message "Undo..."))
(or (eq last-buffer-undo-list buffer-undo-list)
;; skip one undo boundary and all point setting commands up
;; until the next undo boundary and try again.
(let ((p buffer-undo-list))
(and (null (car-safe p)) (setq p (cdr-safe p)))
(while (and p (integerp (car-safe p)))
(setq p (cdr-safe p)))
(eq last-buffer-undo-list p))
(progn (undo-start)
(undo-more 1)))
(undo-more (or arg 1))
;; Don't specify a position in the undo record for the undo command.
;; Instead, undoing this should move point to where the change is.
;;
;;;; The old code for this was mad! It deleted all set-point
;;;; references to the position from the whole undo list,
;;;; instead of just the cells from the beginning to the next
;;;; undo boundary. This does what I think the other code
;;;; meant to do.
(let ((list buffer-undo-list)
(prev nil))
(while (and list (not (null (car list))))
(if (integerp (car list))
(if prev
(setcdr prev (cdr list))
;; impossible now, but maybe not in the future
(setq buffer-undo-list (cdr list))))
(setq prev list
list (cdr list))))
(and modified (not (buffer-modified-p))
(delete-auto-save-file-if-necessary recent-save)))
(or (eq (selected-window) (minibuffer-window))
(message "Undo!"))
(setq last-buffer-undo-list buffer-undo-list))
帮助系统
使用 Emacs 的过程中,您随时可以获取帮助
M-x help-with-tutorial
C-h t
Emacs快捷指南
M-x info-emacs-manual
C-h r
Emacs使用手册
M-x info
C-h i
在线帮助
其它
表 25.2. Emacs帮助系统
C-h a | M-x apropos-command | 搜索命令 |
C-h f | M-x describe-function | 函数说明 |
C-h v | M-x describe-variable | 变量说明 |
C-h k | M-x describe-key | 键绑定说明 |
C-h c | M-x describe-key-briefly | 键绑定说明 |
C-h w | M-x where-is | 查找键绑定 |
- 使用帮助时,可能会分割出其它窗格。
C-x 1
关闭其它窗格 详见 “窗格和缓冲区管理”一节
基本操作
针对文件及编辑器的一些操作,绝大多数软件中,这类操作都安排在 文件 菜单里面。
表 25.3. Emacs 基本操作
C-x C-c | M-x save-buffers-kill-emacs | 保存退出 |
C-x C-z | M-x iconify-or-deiconify-frame | 挂起(最小化) |
C-x C-f | M-x find-file | 打开文件、目录 |
C-x C-r | M-x find-file-read-only | 以只读模式打开 |
C-x i | M-x insert-file | 插入文件 |
C-x C-s | M-x save-buffer | 保存 |
C-x s | M-x save-some-buffers | 询问,保存所有未保存的缓冲区 |
C-x C-w | M-x write-file | 另存为文件 |
C-x RET r | M-x revert-buffer-with-coding-system | 以指定编码读取文件 |
C-x RET f | M-x set-buffer-file-coding-system | 以指定编码保存文件 |
M-x revert-buffer | 恢复到原始状态 |
数字参数
Emacs 中可以使用 Ctrl+u 向命令传递参数。例如用数字作为参数,指定命令运行的次数
C-u (#) command
M-x universal-argument (通用参数)
例如:
C-u 10 C-f 向前10个字符
C-u 10 M-x forward-char
M-(#) (command)
negative-argument (负参数)
M-[1-9] 快速参数
digit-argument (数字参数)
基础编辑
几乎所有编辑器都具有的基础功能。
使用 Readline 控件的程序,例如 bash ;以及其它使用 Emacs风格 键绑定的程序,也使用基本相同的功能键。如果熟悉 bash 的快捷键,这些绑定您一定驾轻就熟
注意:这里只是一个列表,更详细的介绍,请参阅《Emacs 快捷指南》 C-h t
定位
表 25.4. Emacs 定位
向前 | 向后 | 向下 | 向上 | |
---|---|---|---|---|
卷屏 | C-v | M-v | ||
字符 | C-f | C-b | C-n | C-p |
单词 | M-f | M-b | ||
行 | C-a | C-e | 移动到行首或行尾,不能跨行 | |
句 | M-a | M-e | ||
段落 | M-{ | M-} | ||
缓冲区 | M-< | M-> | 移动到缓冲区起始或结束 | |
行号 | M-g g | M-g M-g | M-x goto-line | 按行号跳转 |
字符位置 | M-x goto-char | 按字符跳转 |
其它:
C-M-l
(M-x reposition-window)
将当前行卷至页面中部
C-l
(M-x recenter)
刷新页面,将将当前行卷至页面中部 (使用数字参数指定行)
M-r M-x
(move-to-window-line)
移动光标至页面中间的行 (使用数字参数指定行)
删除
表 25.5. Emacs 删除
向前 | 向后 | |||
---|---|---|---|---|
字符 | C-d | M-x delete-char | DEL | M-x delete-backward-char |
单词 | M-d | M-x kill-word | C-Delete / M-DEL | M-x backward-kill-word |
行 | 光标至行末 | C-k | M-x kill-line | |
整行 | C-S-backspace | M-x kill-whole-line | ||
按表达式删除 | C-M-k | M-x kill-sexp | ||
区块 | C-w | M-x kill-region | ||
空白 | 删除连续空格 | M-x delete-horizontal-space |
注意:上表中 DEL 实际按键为 Backspace ,PC 中只有 Delete 键,而没有 DEL 键,Emacs 把 Backspace 映射为 DELbackspace 实际按键也为 Backspace类似的,Emacs 把 PC 的 Enter 键映射为 RET ; 而 RET 实际为 C-m
可能您注意到了,Emacs 进行删除时有两种处理方法, delete 和 kill
kill
比较类似于 剪切 ,剪切掉的内容被依次放入 剪切队列 kill-ring ,可以召回。
delete
就是 删除 了,删除掉的内容并不能召回。但是可以通过 M-x undo 撤消删除。
复制
M-w
(M-x kill-ring-save)
将内容放入 剪切队列 kill-ring
C-w
剪切
粘贴
C-y
(M-x yank)
从 剪切队列 kill-ring 中召回最后一次放入的内容
M-y
(M-x yank-pop)
从 剪切队列 kill-ring 中按后进先出的顺序,依次召回
- 这个命令只能在 M-x yank 或者 M-x yank-pop 之后使用。也就是说,只能 C-y 后 M-y , M-y 可以连续多次。
撤消
C-/
(M-x undo)
撤消之前的修改
C-_
(M-x undo)
C-x u
(M-x advertised-undo)
advertised-unde 是 undo 命令的一个别名
- 为了减少 undo 的次数,每插入20个字符,视为一个 undo 的单位。
重做
安装 redo.el 扩展,并在配置文件中添加如下内容
;;---------- redo
;; 读取扩展
(require 'redo)
;; 设置 Redo 的键绑定
(global-set-key ( kbd "C-.") 'redo)
区块编辑
如何选中区块,可以参考“区块选择”一节
需要补充的是,完成区块选择时,实际定义了两种区块 :
---XXXXXxxx
xxxXXXXXxxx
xxxXXXXXxxx
xxxXXXXX---
---代表没有被选中的区域
连续区块为标记和光标点之间连续的区块;字符 X 和 x 均为连续区块
矩形区块为标记和光标点之间矩形的区块;大写字符 X 为矩形区块
表 25.6. Emacs 区块编辑
连续区块 | ||
C-SPC | M-x set-mark-command | 在光标点处设置标记 |
C-@ | 同上 | 建议使用 C-t |
M-@ | M-x mark-word | 在单词结尾处设置标记 |
M-h | M-x mark-paragraph | 选中段落 |
M-x mark-end-of-sentence | 在句末设置标记 | |
C-x h | M-x mark-whole-buffer | 整个缓冲区 |
C-x C-x | M-x exchange-point-and-mark | 交换标记和光标点 |
C-w | M-x kill-region | 剪切区块 |
M-w | M-x kill-ring-save | 复制区块 |
C-y | M-x yank | 粘贴区块 |
M-y | M-x yank-pop | 队列粘贴 |
矩形区块 | ||
C-x r k | M-x kill-rectangle | 删除矩形区块 |
C-x r y | M-x yank-rectangle | 粘贴上一次删除的矩形区块 |
C-x r t | M-x string-rectangle | 用指定字符填充 |
C-x r o | M-x open-rectangle | 用空格插入 |
C-x r c | M-x clear-rectangle | 用空格填充 |
C-x r r | M-x copy-rectangle-to-register | 拷贝到寄存器中 |
搜索和替换
Emacs中,默认使用 增量搜索 :在搜索对话模式中输入关键词的同时,Emacs 就开始进行搜索,随着关键字的输入,不断的缩小搜索范围
而传统的非增量搜索,则是关键词输入后,再进行搜索。
表 25.7. Emacs 搜索
增量搜索 | ||
C-s | M-x isearch-forward | 向前增量搜索 |
C-r | M-x isearch-backward | 向后增量搜索 |
C-M-s | M-x isearch-forward-regexp | 正则表达式向前增量搜索 |
C-M-r | M-x isearch-backward-regexp | 正则表达式向后增量搜索 |
询问替换 | ||
M-% | M-x query-replace | 询问替换 |
C-M-% | M-x query-replace-regexp | 正则表达式询问替换 |
搜索 | ||
M-x search-forward | 向前搜索 | |
M-x search-backward | 向后搜索 | |
M-x search-forward-regexp | 正则表达式向前搜索 | |
M-x search-backward-regexp | 正则表达式向后搜索 | |
替换 | ||
M-x replace-string | 替换 | |
M-x replace-regexp | 正则表达式替换 |
- 增量搜索时,关键词会被一直保留。可以直接进行下一次搜索
- 下一次增量搜索,如果之前进行了其它操作,则需要连续两次命令(快捷键),才能召回关键词。
C-g
取消搜索,回到搜索前的位置RET
结束搜索,停在当前位置- 可以选中区块后,在区块内进行替换
其它
插入控制字符
使用 C-q
,可以在缓冲区插入一个控制字符。例如:
C-q C-m = ^M
文本换位
表 25.8. Emacs 其它
字符 | C-t | M-x transpose-chars |
单词 | M-t | M-x transpose-words |
行 | C-x C-t | M-x transpose-lines |
将 TAB 字符转换为空格
选中需要转换的区域, M-x untabify
对齐文本块
选中需要对齐的区域, M-x indent-region
窗格和缓冲区管理
多窗格
表 25.9. Emacs 窗格
C-x 2 | M-x split-window-vertically | 分隔出两个垂直窗格,水平分隔线 |
C-x 3 | M-x split-window-horizontally | 分隔出两个水平窗格,垂直分隔线 |
C-x 1 | M-x delete-other-window | 只保留当前窗格 |
ESC ESC ESC | M-x keyboard-escape-quit | 只保留当前窗格 |
C-x 0 | M-x delete-window | 关闭当前窗格 |
C-x o | M-x other-window | 在下一个窗格中激活光标 |
C-M-v | M-x scroll-other-window | 向下卷动下一个窗格,使用负参数可以向上卷动 |
- 下一个窗格: 垂直分隔,则先左后右;水平分隔,则先上后下。如果窗格还有子窗格,则先遍历其子窗格后,再遍历其它窗格,以此递归。
多缓冲区
Emacs 中,打开新的缓冲区,原有缓冲区并不会关闭
表 25.10. Emacs 缓冲区
C-x C-b | M-x list-buffers | 查看缓冲区列表 |
C-x b | M-x switch-to-buffer | 切换到其它缓冲区 |
C-x k | M-x kill-buffer | 关闭当前缓冲区 |
- 切换到其它缓冲区时,默认上一次使用的缓冲区(可以用 TAB 补全)
- 使用多窗格时,缓冲区操作只对当前窗格有效
建议使用 ibuffer.el 这个扩展。 Emacs 自带,在配置文件中添加如下语句
;;========== ibuffer (require 'ibuffer) (global-set-key ( kbd "C-x C-b ")' ibuffer)
另一个缓冲区列表的扩展(Emacs 自带)
;;CRM bufer list (global-set-key "\C-x\C-b" 'electric-buffer-list)
寄存器管理
寄存器用于存贮内容,在需要时取出,插入缓冲区。
Emacs 的寄存器使用单个字符命名,可以存贮两种内容 :
光标位置和窗口状态
表 25.11. Emacs 寄存器
C-x r SPC (寄存器名) | M-x point-to-register | 存贮光标位置 |
C-x r w (寄存器名) | M-x window-configuration-to-register | 保存当前窗口状态 |
C-x r f (寄存器名) | M-x frame-configuration-to-register | 保存所有窗口状态 |
C-x r j (寄存器名) | M-x jump-to-register | 光标跳转 |
C-x j (寄存器名) | 略…… | 同上 |
文本和数字
表 25.12. Emacs 寄存器2
C-x r s (寄存器名) | M-x copy-to-register | 将连续区块拷贝到寄存器中 |
C-x r r (寄存器名) | M-x copy-rectangle-to-register | 将矩形区块拷贝到寄存器中 |
C-u (数字) C-x r n (寄存器名) | M-x number-to-register | 将数字拷贝到寄存器中 |
C-x r i (寄存器名) | M-x insert-register | 在缓冲区中插入寄存器内容 |
- M-x view-register 查看寄存器内容
- M-x list-registers 查看寄存器列表
- 寄存器中的矩形区块,以矩形区块的方式插入到缓冲区中。 见 “区块编辑”一节
也可以将文件插入到寄存器中 (set-register ?寄存器名称 '(file . 文件名)) ,示例
M-x lisp-interaction-mode 进入交互模式,输入如下 Lisp 代码: (set-register ?e '(file . "~/.emacs"))(光标)移动此外, C-j 求值。 M-x list-registers 查看寄存器列表,多了寄存器 e : Register e contains the file "~/.emacs".
书签管理
Emacs 可以在当前位置创建一个书签,以便能够快速的返回。
与存储光标位置的寄存器略有不同
- 书签可以使用单词来命名,而不限于一个字符。起一个容易记住的名字
- 退出 Emacs 后,书签不会消失,下次还可以使用
表 25.13. Emacs 书签
C-x r m (name) | M-x bookmark-set | 设置书签 |
C-x r b (name) | M-x bookmark-jump | 跳转到书签 |
C-x r l | M-x bookmark-bmenu-list | 书签列表 |
M-x bookmark-delete | 删除书签 | |
M-x bookmark-load | 读取存储书签文件 |
- 书签默认存储在
~/.emacs.bmk
文件中 在配置文件中,可以设置书签存储的文件
;; 书签文件的路径及文件名 (setq bookmark-default-file "~/.emacs.d/.emacs.bmk") ;; 同步更新书签文件 ;; 或者退出时保存 (setq bookmark-save-flag 1)
Shell 模式
M-x shell
进入 Shell 模式,可以完成一些简单的工作。不过有些情况下,输出会有一些问题
事实上,这是 Emacs 自带的终端。它与 bash 和 sh 的兼容比较好,而 fish 之类比较现代的 Shell,在 Emacs 终端里的效果则很差
需要注意的是, readline-bash 的绑定 C-p C-n ,在 Emacs 终端需要使用 M-p M-n 。其它的键绑定,也以 Emacs 为准
宏
记录一系列操作,在需要的时候运行
例如给一个单词加 " ,可以分解为以下操作:
M-b 移动到词首
"
M-f 移动到词尾
"
这种重复的操作往往需要经常执行,手动未免太没有效率。我们可以把这些操作制作成宏,然后运行这个宏
当然,这只是最简单的宏。结合正则表达式进行匹配,以宏进行操作,可以完成许多复杂的操作
定义与运行
表 25.14. Emacs 宏
开始录制 | C-x ( (M-x kmacro-start-macro) | F3 (M-x kmacro-start-macro-or-insert-counter) |
结束录制 | C-x ) (M-x kmacro-end-macro) | F4 (M-x kmacro-end-or-call-macro) |
播放 | C-x e (M-x kmacro-end-and-call-macro) |
宏队列
与 剪切队列 类似,Emacs 中也有 宏队列 的概念: 当一个新的宏被定义,原有的宏并不消失,只是在宏队列中的位置被挤到后面。
C-x C-k
进入宏队列,以下的操作可以在宏队列中连续进行
表 25.15. Emacs 宏队列
基本操作 | ||
C-n | M-x kmacro-cycle-ring-next | 下翻 |
C-p | M-x kmacro-cycle-ring-previous | 上翻 |
C-d | M-x kmacro-delete-ring-head | 删除当前宏 |
C-k | M-x kmacro-end-or-call-macro-repeat | 运行当前宏 |
命名与保存 | ||
n (name) | M-x kmacro-name-last-macro | 命名 |
b | M-x kmacro-bind-to-key | 绑定 |
M-x insert-kbd-macro | 在缓冲区中插入宏定义 | |
宏编辑器 | ||
C-e | M-x kmacro-edit-macro | 编辑 |
e | M-x edit-kbd-macro | 编辑指定名称的宏 |
l | M-x kmacro-edit-lossage | |
询问执行 | ||
q | M-x kbd-macro-query | 在播放宏时,将进行询问确认 |
计数器 | ||
C-i | M-x kmacro-insert-counter | 将宏计数器的数值插入缓冲区 |
C-c | M-x kmacro-set-counter | 为宏计数器设置一个数值 |
C-a | M-x kmacro-add-counter | 给宏计数器添加一个前缀参数 |
C-f | M-x kmacro-set-format | 给宏计数器指定一个将要插入的特殊值 |
- 保存为文件,使用 M-x load-file 加载
- 保存到配置文件中,启动时加载
文件管理
文件管理
C-x d
(M-x dired)
进入 Dired 列表模式
C-x C-d
(M-x list-directory)
获取文件列表(简洁)
C-x C-f
(M-x find-file)
打开文件,没有文件名则打开目录
定位、查看
表 25.16. Emacs 文件管理
向下 | 向上 | ||
---|---|---|---|
文件 | n | p | |
C-n | C-p | ||
SPC | DEL | 上一级 | |
目录 | > | < | ^ |
已标记 | M-} | M-{ |
g | 刷新 |
s | 切换名称/日期排序方式 |
i | 在当前窗口插入一个子目录 |
v | 查看当前文件 |
y | 查看当前文件类型 |
= | 比较 |
标记
普通标记 | m | 标记(显示为字符 * ) |
t | 反向标记 | |
u | 取消标记 | |
U | 取消所有标记 | |
* / | 标记文件夹 | |
标记所有可执行文件 | ||
* @ | 标记所有符号连接 | |
* c | 改变标记的符号 | |
% m | 根据正则表达式标记文件 | |
% g | 根据正则表达式标记文件内容 | |
删除标记 | d | 标记为删除(显示为字符 D ) |
~ | 将备份文件标记为删除 | |
# | 将存盘文件标记为删除 | |
& d | 根据正则表达式标记删除 | |
x | 执行删除 |
操作
RET | 在新缓冲区打开文件 | |
o | 在另一个窗格打开 | |
C-o | 在另一个窗格后台打开 | |
C-x C-f | 新建文件 | |
+ | 新建目录 | |
C-x C-q | 将文件列表设为只读 | |
可以结合 * 标记批量进行 | D | 删除文件 |
C | 拷贝 | |
R | 重命名/移动 | |
O | 改变用户 | |
G | 改变群组 | |
M | 改变权限 | |
S | 符号链接 | |
H | 硬链接 | |
Z | 压缩 | |
T | touch | |
w | 复制文件名 | |
k | 删除行(刷新后恢复) |
服务器模式
由于各种原因,Emacs 启动比较耗时。可以启动一个 Emacs 的守护进程
emacs --daemon
然后通过 emacsclient 来连接服务器
emacsclient -t --alternate-editor jed file
- -t 在当前控制台打开 emacs 窗口
- --alternate-editor jed 如果不能连接到 emacs 服务器,则使用 jed 编辑器
也可以使用 Emacs 服务器模式,M-x server-start
或者在配置文件中添加 (server-start) 启用 Emacs 服务器,使用 emacs-client 连接。
大纲模式
Emacs 的大纲模式(Outline mode),是一个十分有用的模式。如果工程规模比较大,你应该用大纲来组织它。
大纲模式通常作为辅模式使用,M-x outline-minor-mode启用。
大纲模式可以根据代码的语法对结构进行识别,但是这种自动模式工作的不是很好,而且不够灵活
另一种工作方式是查找特定的字串,来组织文档的结构。这种工作方式是可控制的,不过需要手动加入这些作为结构标识的字串。似乎有点麻烦,但是对于严谨的构思来说,你是需要列一个提纲的
大纲模式默认以行首的 *
作为结构标识,每多一个 *
就是下一级分支。
大纲模式默认的键绑定太过复杂,在配置文件中添加以下语句,将所有大纲模式操作的前缀键改为 Ctrl+o
(setq outline-minor-mode-prefix [(control o)])
试着在文档中随便加入几个这样的标识
*主干一
**分支一
**分支二
*主干二
进行一些简单的操作
C-o C-a | 全部显示 |
C-o C-t | 显示主干 |
C-o C-q | 全部隐藏 |
C-o C-i | 显示下一级分支 |
C-o C-k | 显示分支 |
C-o C-e | 显示节点 |
C-o C-d | 隐藏分支 |
定制结构标识
为了不影响代码的功能,通常要把结构标识放到注释中,这样作有可能会带来不便,例如在 XML 环境中就要像这样使用:
<!--
*结构标识
-->
这样会给你的工作制造出一些混乱,并且大纲模式是以行为单位进行识别的,也就是说<!--
属于上一个分支
好在大纲模式可以通过正则表达式来定义结构标识,你可以把结构标识定义为下面这种形式
<!--*结构标识-->
通过设置outline-regexp
定制标识:
setq outline-regexp "<!\\-\\-\\*+
配置
完整的配置:
例 25.2. emacs 大纲模式
(setq outline-minor-mode-prefix [(control o)])
(require 'docbook-xml-mode)
(add-hook 'docbook-xml-mode-hook
(function (lambda ()
(setq outline-regexp "<!\\-\\-\\*+")
(outline-minor-mode)
(hide-body)
)))
添加 docbook-xml-mode 钩子,运行下面代码[47]
操作列表
显示、隐藏
全部显示 | 显示分支 | 隐藏 | ||||
---|---|---|---|---|---|---|
全局 | show-all | C-o C-a | hide-body | C-o C-t | hide-sublevels | C-o C-q |
分支 | show-subtree | C-o C-s | hide-leaves | C-o C-l | hide-subtree | C-o C-d |
show-branches | C-o C-k | |||||
show-children | C-o C-i | |||||
节点 | show-entry | C-o C-e | hide-entry | C-o C-c | ||
其它分支 | hide-other | C-o C-o |
移动
向前 | 向后 | |||
---|---|---|---|---|
全局 | outline-next-visible-heading | C-o C-n | outline-previous-visible-heading | C-o C-p |
同级 | outline-forward-same-level | C-o C-f | outline-backward-same-level | C-o C-b |
返回上一级 | outline-up-heading | C-o C-u |
[47]
docbook-xml-mode.el
中定义 docbook-xml-mode-hook,模式启动时运行钩子代码
在 Emacs 中使用 sdcv
在 Emacs 配置文件中加入以下代码
(global-set-key (kbd "C-c d") 'kid-sdcv-to-buffer)
(defun kid-sdcv-to-buffer ()
(interactive)
(let ((word (if mark-active
(buffer-substring-no-properties (region-beginning) (region-end))
(current-word nil t))))
(setq word (read-string (format "Search the dictionary for (default %s): " word)
nil nil word))
(set-buffer (get-buffer-create "*sdcv*"))
(buffer-disable-undo)
(erase-buffer)
(let ((process (start-process-shell-command "sdcv" "*sdcv*" "sdcv" "-n" word)))
(set-process-sentinel
process
(lambda (process signal)
(when (memq (process-status process) '(exit signal))
(unless (string= (buffer-name) "*sdcv*")
(setq kid-sdcv-window-configuration (current-window-configuration))
(switch-to-buffer-other-window "*sdcv*")
(local-set-key (kbd "d") 'kid-sdcv-to-buffer)
(local-set-key (kbd "q") (lambda ()
(interactive)
(bury-buffer)
(unless (null (cdr (window-list))) ; only one window
(delete-window)))))
(goto-char (point-min))))))))
Windows 下字体设置
(set-default-font "Verdana-10")
(if window-system
(set-fontset-font (frame-parameter nil 'font)
'unicode '("simsun" . "unicode-bmp"))
)
版本
在 Linxu 系统中,Emacs 的最新版本通常为 emacs-snapshot、emacs-cvs
Emacs for Windows 请到这里下载,推荐“patched”版本