elisp
elisp基本语法
help
- M-x apropos (find symbol)
- C-h k(descripte key)
- C-h f (function)
- C-h v (variable)
control flow
if
1
2
3
4(if test
a
b)
;;test is the conditionwhen
1
2
3(when condition a b c)
;;; equivalent
(if condition (progn a b c) nil)unless
1
2
3
4(unless condition a b c)
;;;equvalent
(if condition nil (progn a b c))cond
和scheme类似:1
2
3
4
5
6(cond ((numberp x) x)
((stringp x) x)
((bufferp x)
(setq temporary-hack x) ; multiple body-forms
(buffer-name x)) ; in one clause
((symbolp x) (symbol-value x)))and
ornot
logical expression: the functionor
works like the
logical “or” in most languages: if all the arguments are false, it
return nil, otherwith it will return the value of the last argument
whose value is non-nil. eg: (or nil nil 3 2 1) return 3 (not t) so
(if a a b) is identical to (or a b)while
Special Form: while condition forms…dolist
Macro: dolist (var list [result]) body…1
2
3
4(defun reverse (list)
(let (value)
(dolist (elt list value)
(setq value (cons elt value)))))dotimes
1
2(dotimes (i 100)
(insert "I will not obey absurd orders\n"))
let let
let
, let*
, letrec
的作用语法与scheme类似
1 |
|
function
1 |
|
optional代表参数可选, rest代表可变参数, 如果有那么会是一个列表
apply
:apply function &rest arguments
调用函数,
function
的值是一个symbol(比如你要调用list
函数,那么你应 该传递 ‘list
)最后一个参数必须是列表, 这个列表参数会被apply自动拆开,然后 传递给functionfuncall
: 与apply
不同的是,它不会拆开列表参数, 而是直接将列表参数作为一个 参数传递给function1
2
3(setq f 'list)
(apply f 'a 'b '(1 2)) ; (a b 1 2)
(funcall f 'a 'b '(1 2)) ; (a b (1 2))
lists
null
: 测试list是否为空listp
: 是否为listcar
cdr
cons
list
: (list ‘a “b” 1) ==> (a “b” 1)append
: (append ‘(a b) ‘(c d)) ==> (a b c d)reverse
: (reverse ‘(a b c d)) ==> (d c b a)nthcdr
: call cdr n timeslength
: get the length of the listmapcar
:equal
: only test the object’s structure and contenteq
: test if the two arguments is the same object(like pointer in
C)
以上的函数都不会原地改变list, 它会返回一个新的list作为结果, 下面的函数会 原地改变list
setcar
: change the car-element of the listsetcdr
: change the cdr-element of the listpush
: 和cons类似,只是会原地改变listpop
: 和car类似,只是会原地改变list(删除list的第一个元素)add-to-list
: 和push类似
Associate List
1 |
|
assoc
: 当list类似于关联数组时,用key来寻找value,用equal测试keyassq
: 与assoc相似,只是用eq测试key
property list
1 |
|
常用的函数
字符串操作
length
: (length “abc”) –> return 3substring
: (substring “hello world” 1 3) –> return “ell”replace-regexp-in-string
: (replace-regexp-in-string regex replace
str) –> 正则替换string-match
: (string-match regex str) –> return the index first
match the regex in str1
2
3
4
5
6(setq mystr "The quick fox jumped quickly.")
(string-match "\\(qu\\)\\(ick\\)"
mystr) ;=>副作用:将匹配结果存储到match-data中
(match-string 0 mystr) ;=>结果为quick,也就是模式匹配的结果
(match-string 1 mystr) ;=>结果为qu ,也就是第一个分组的匹配结果
(match-string 2 mystr) ;=>结果为ick ,也就是第二个分组的匹配结果
Regex
- 用来匹配字符串时 ^匹配字符串的开头, $匹配字符串的结尾, 用来匹配buffer中的 内容时, ^匹配一行的开头, $匹配一行的结尾.
- 常用的字符串匹配函数是string-match, 常用的buffer 内正则匹配的函数是
looking-at
它会匹配从当前光标开始的内容. - 在通常的语言(比如python)的正则表达式实现中, (, ), [, ], {, }, \ 等都是特 殊字符, 但是在emacs
lisp中都是普通的字符,所以如果你要把它们当作特殊的字符, 比如用(,)来分组, 那么你就必须对(,)转义,
也就是使用( 与 ), 可是由于 一个普通字符, 所以当你希望它是一个用来转义的特殊字符时, 你必须对
义(否则), 结果就是\(, \)这样丑陋的东西. 这是 一个设计失误. - M-x re-builder: 可以实时的显示buffer中匹配regex的字符串.
buffer相关: buffer
buffer-name
: (buffer-name)buffer-file-name
: (buffer-file-name) –> full namesave-buffer
: (save-buffer) –> 保存当前的buffer 的文件kill-buffer
: (kill-buffer BufferName)kill-this-buffer
: (kill-this-buffer)with-current-buffer
: (with-current-buffer BUFFER-OR_NAME body)
–> 将 BUFFER-ORNAME指定的buffer设为当前buffer, body运行完后会恢复原先的buffer, *
常用*
buffer content相关
- 增:
insert
- 删:
delete-char
,delete-region
等等一系列的delet函数 - 查找:
re-search-forward
与re-search-backward
最 常用 - 替换: 一般是查找后,使用
replace-match
来替换 - 获取buffer的内容:
buffer-substring
, point上的字符可以用char-after
与char-before
来获得, point附近的词可以用current-word
得到, 其它类 型的文本可以用thing-at-point
.
buffer中移动以及光标相关 point
point
:(point)point-max
:(point-max) –> 一般返回buffer-end,但是如果指定了Narrowing,
那么结果就不同point-min
:(point-min) –> 一般返回1,但是如果指定了Narrowing,那么结果就 不同buffer-end
:(buffer-end)buffer-size
:(buffer-size)save-excursion
:(save-excursion body) –> 可以用来保存当前的point,不管
body中如何改变point,执行完成后都会回到执行save-excursion之前的pointnarrow-to-region
:(narrow-to-region start end) –>
Narrowing(将emacs的 文本操作限定在buffer的一个子区域中)region-beginning
:(region-beginning)region-end
:(region-end) –> linebeginning-of-line
:(beginning-of-line)end-of-line
:(end-of-line)buffer-substring
:(buffer-substring start end)goto-char
:(goto-char 293)forward-char
:(forward-char n)backward-char
:(backward-char n)skip-chars-forward
:(skip-chars-forward “”͡)skip-chars-backward
:(skip-chars-backward “”͡)forward-line
:(forward-line n)backward-line
:(backward-line n)looking-at
:(looking-at regex) ;; return t if text after the point
match the REGEXPlooking-back
:(looking-back regex)search-forward
:(search-forward my-str)search-backward
:(search-backward my-str)re-search-forward
:(re-search-forward my-regex)re-search-backward
:(re-search-backward my-regex)replace-match
:(replace-match)
file相关 file
find-file
: (find-file path) ;;open a filewrite-file
: (write-file path) ;;save the fileinsert-file-contents
: (insert-file-contents path) –>将指定文件内容插入当前位置append-to-file
: (append-to-file start-pos end-pos path)rename-file
: (rename-file old-name new-name)copy-file
: (copy-file file-name new-name)delete-file
: (delete-file file-name)file-name-directory
: (file-name-directory full-path) –>路径(不包含文件名)file-name-nondirectory
: (file-name-nondirectory full-path)
–>文件名(不包含路径)file-name-extension
: (file-name-extension file-name) –>后缀,一般时扩展名file-name-sans-extension
: (file-name-sans-extension
“/hello/abc.html”) –>return “/hello/abc”,只去掉后缀
other useful function
save-excursive
: 保存并恢复当前pointinteractive
:thing-at-point
:1
2
3(thing-at-point 'word)
(thing-at-point 'sexp)
(thing-at-point 'url)bounds-of-thing-at-point
: 和thing-at-point
类似,只是它会返回一个 pair –>
(start . end) , 这个pair 的car是左边界,cdr是右边界current-time-string
: (current-time-string) –> 返回当前时间字符串format-time-string
: (format-time-string “%1.%M %p” (current-time))symbol-name
: 将symbol转换为字符串, eg: (symbol-name ‘sym) –> “sym”intern
: 将字符串转换为symbol, eg: (intern “sym”) –> ‘sym
useful variable
mark-active
: 现在一般用region-active-p
代替last-command
: 最后一条命令
useful code snippet
test if a mode(major or minor) is on (flymake-mode as an example)
1
2
3(if (and (boundp 'flymake-mode) flymake-mode)
(message "flymake-mode is on")
(message "flymake-mode is off"))setting keybinding for specified mode there are two way, the
first is eval-after-load1
2
3
4
5
6(eval-after-load "org"
'(progn
(define-key org-mode-map (kbd "<C-M-return>") 'org-insert-heading-respect-content)
(define-key org-mode-map (kbd "<M-right>") nil) ; erasing a keybinding.
(define-key org-mode-map (kbd "<M-left>") nil) ; erasing a keybinding.
))the second way is add-hook
1
2
3(defun my-org-settngs ()
(flyspell-mode 1))
(add-hook 'org-mode-hook 'my-org-settngs)对以上二者的说明:
- eval-after-load只运行一次,所以比较适合那些一次性的设置,比如给某个特定的mode设置keymap,它没有当前
buffer的概念,
而add-hook这是对每一个打开了该mode的buffer都会运行一次.所以它适合来设置一些和buffer相
关的设置 - local-set-key 实际上也是调用 define-key , 其中 map 参数是
(current-local-map) 的值. 这个值 一般是由major mode设置的. 所以在给minor
mode设置keymap时,使用 define-key 比较好, 因为可以直接指定 minor mode 的map
- eval-after-load只运行一次,所以比较适合那些一次性的设置,比如给某个特定的mode设置keymap,它没有当前
interactive interactive
emacs中交互式运行的命令都需要在函数体的最上方加上(interactive arg-descriptor),
arg-descriptor最后会解析成参数从递给该命令, arg-descriptor 有三种形式
(interactive)
:arg-descriptor
为nil
, 该命令没有参数(interactive "p\ncZap to char: ")
:arg-descriptor
以\n
分割后每一
部分都会产生一个参数,比如上面的例子,以:1
2"p"
"cZap"其中每一部分开头的那个字母有特殊的含义:
- p: 接受C-u传入的参数,返回的是整数,默认是1,也就是numeric prefix argument.
后面不能跟提示字符串,因为参数来自C-u而不是minibuffer - c: 从minibuffer中读入一个字符,它后面跟的Zap会作为提示显示在minibuffer 中
除了上面的p, c外还有很多这样的特殊字符,下面是一些常用的,
这是官方文档
, 这是一些单词的含义: Prompt (后面可以跟显示在minibuffer中提示字符串), Existing
(file, command… 必须已存在), Completion (可以补全) :- P(uppercase): 和小写p的最大区别是它返回的是raw prefix argument, 默认是 nil,
后面不能跟提示字符串,因为参数来自C-u而不是minibuffer - b: 已存在的buffer name, 可以跟提示字符串, 默认是当前buffer, 可以补全
- B: 不存在的buffer name, 可以跟提示字符串, 默认是最近使用的buffer
- f: 已存在的文件名, [Existing, Completion, Default, Prompt]
- F: 不存在的文件名, [Completion, Default, Prompt]
- C(uppercase): 命令名称, [prompt, Existing, Completion]
- p: 接受C-u传入的参数,返回的是整数,默认是1,也就是numeric prefix argument.
(interactive (lisp-expression-return-list-argument))
:
arg-descriptor是 一个elisp表达式,这个表达式应该产生传递给命令的参数,一般会调用read-string
这样的函数从minibuffer中读入字符串1
2
3
4
5
6
7
8(interactive (list
(read-string (format "word (%s): " (thing-at-point 'word))
nil nil (thing-at-point 'word))))
(interactive
(let ((string (read-string "Foo: " nil 'my-history)))
(list (region-beginning) (region-end) string)))这是
read-string
的原型:1
read-string prompt &optional initial history default inherit-input-method
autoload autoload
使用autoload可以使一个函数或者命令只在调用时才加载进来,这样可以加快emacs的 启动时间,使用autoload有两种方法:
autoload function filename &optional docstring interactive type
该函数会将function标记为autoload:
- function: 一个symbol,用来代表函数或者宏
- filename: 一个
string
, 用来指定加载function的文件,不要带目录名与后缀 名 - docstring: 文档字符串,指定后可以在没有加载function的情况下看它的文档
- interactive: bool,如果为
t
那么就是一个command,否则就是一个函数 - type:
使用魔法注释
1
2
3
4
5
6;;;###autoload
(defun doctor ()
"Switch to *doctor* buffer and start giving psychotherapy."
(interactive)
(switch-to-buffer "*doctor*")
(doctor-mode))那么这个
doctor
就是标记为autoload的.实际上上面的注释部分会被自动转换 为autoload
的形式package.el 中的
autoload
, 以yasnippet为例, package.el 会根据yasnippet
中魔法注释创建一个yasnippet-autoloads.el的文件,该文件就是调用 autoload函
数来自动加载yasnippet.el中指定要自动加载的函数,而
yasnippet-autoloads.el(所有的package.el管理的包的 -autoloads.el文件)都是是在
执行(package-initialize)时运行的.这样每一个包需要自动加载的函数就都加载 进来了
define mode(minor mode or major mode)
keymap
定义一个mode的时候,通常会用到keymap,keymap规定了该mode下的键映射,可以用如 下的代码来创建一个keymap:
1 |
|
上面就创建了一个名为 prelude-mode-map的keymap
minor mode
创建一个minor mode,需要经过以下几个步骤:
- 定义一个变量 xxxx-mode,该变量为 mode varible,如果该 mode已打开,那么这个 变量就为t,否则为nil,
这个变量一般要设为buffer local varible - 定义一个命令 xxxx-mode, 注意命令的名字必须和第一步的变量的名字相同,该命 令的作用就是设置第一步的mode
varible,当然该命令中也可以添加一些初始化的 操作 - 添加一个元素到 minor-mode-alist
上面的步骤比较繁琐,这里有一个宏, define-minor-mode可以方便的定义一个minor mode
1 |
|
“ Pre”是要在modeline上显示的字符串,prelude-mode-map是该minor-mode的keymap,
下面的body,实际就是 prelude-mode命令了, 可以通过
define-globalized-minor-mode 来把minor
mode设为global,可以参考这里的代码
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!