为了定制emacs,emacs lisp不能不学。既能学习一门新的语言,又能更随心所欲地定制emacs,何乐不为。
本文为阅读Practical Emacs Lisp 的读书笔记。
学习环境 emacs lisp最好的开发环境就是emacs,因为这里就是他的运行环境。
默认打开emacs会包含一个*scratch*
buffer,这个buffer默认就是Lisp Interaction mode,可以用来执行lisp脚本。
在这个buffer中输入(+ 1 2)
,这是emacs种最简单的表达式,有许多种方法执行他:
C-j
eval-print-last-sexp
执行光标前的S表达式,并输出到当前buffer
C-x C-e
eval-last-sexp
执行光标前的S表达式,把值输出到echo area(屏幕下边)
C-M-x
eval-defun
valuate the top-level form containing point, or after point.
我个人比较喜欢使用C-M-x
,因为不用把鼠标移到表达式后。
输出 1 2 3 4 5 6 7 8 (message "hello world") "hello world" (message "print number %d" 100) "print number 100" (message "print %s" "string") "print string" (message "print list %S" (list 1 2 3)) "print list (1 2 3)"
算数函数 lisp使用的是中缀表达式,所以写起来会比较奇怪。而lisp的精髓也就是这S表达式了。
1 2 3 4 5 6 7 8 9 10 11 12 (+ 1 2) 3 (+ 1 2 3) 6 (/ 7 2) 3 (/ 7 2.0) 3.5 (% 7 4) 3 (expt 2 3) 8
判断数据类型:
1 2 3 4 5 6 7 8 (integerp 1) t (integerp 1.) ;; 1.是一个整数 t (integerp 1.0) nil (floatp 1.0) t
p结尾的函数表示返回true或者false的函数,p指的是predicate
。
字符串和数字互转:
1 2 3 4 (string-to-number "3") 3 (number-to-string 3) "3"
True和False 在elisp中,nil
表示false,其他的非nil的指都是true。
注意:
nil
和空list()
等价,所以()
也是false
0
是true
""
是true
为了表示方便,标示符t
用来
布尔函数 逻辑关系:
1 2 3 4 5 6 (and t nil) nil (or t nil) t (not t) nil
相等关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 (< 1 2) t (> 1 2) nil (>= 1 2) nil (<= 1 2) t (= 3 3) t (= 3 3.0) t (/= 3 4) ;;elisp的不等于的写法比较特殊。不等于只能用于数字。 t (string-equal "a" "a") ;;/=只能用于比较数字,比较字符串需要使用string-equal t (equal 1 "1") ;;equal是更通用的比较,但是需要两者数据类型和值都一样 nil (equal 1 1) t (equal 1 1.0) ;;这个不一样是因为数据类型不一样 nil
奇偶关系,没有原生函数:
1 2 3 4 (= (% 8 2) 0) ;;偶数 t (= (% 8 2) 1) ;;奇数 nil
变量 全局变量:
1 2 3 4 (setq x 1) ;; 把1付给q 1 (setq a 1 b 2 c 3) ;; 把123付给abc 3
defvar和setq的区别:
defvar只对未赋值的变量赋值
defvar可以对变量设置文档字符串
The defvar special form is similar to setq in that it sets the value of a variable. It is unlike setq in two ways: first, it only sets the value of the variable if the variable does not already have a value. If the variable already has a value, defvar does not override the existing value. Second, defvar has a documentation string.Programming in Emacs Lisp: defvar
条件语句 1 2 3 4 5 6 7 8 9 10 11 12 ;;if的结构是`(if test true_body false_body)`,注意每个body都是一个表达式。 (if (= 1 1) "equal" "not equal") "equal" ;;如果逻辑超过一个表达式,可以使用progn把多个表达式组织成一个表达式 (if (= 1 1) (progn (message "hello") (message "world"))) "world" ;;如果不需要else子句,可以使用when ;;(when test expr1 expr2 …)相当于(if test (progn expr1 expr2 …)) (when (= 1 1) (message "hello") (+ 1 2)) 3
块语句 (progn (message "a") (message "b"))
把多个表达式组合成一个表达式,上面已经提到了。progn返回最后表达式的值。
循环语句 1 2 3 4 5 6 7 8 9 10 (setq x 0) 0 (while (< x 4) (print (format "%d" x)) (setq x (1+ x))) "0" "1" "2" "3" nil
white返回的一定是nil
函数 定义函数的格式如下:
(defun function_name (param1 param2 …) "doc_string" body)
1 2 3 4 (defun add (a b) (+ a b)) (add 2 3) 5
普通函数是无法作为命令(command)调用的,如果你想使用M-x
(execute-extended-command
)来调用你的函数,可以这么写:
1 2 3 (defun say () (interactive) (message "hello"))
如果函数需要参数,可以这么写,然后通过C-u 1 M-x say
来调用:
1 2 3 (defun say (name) (interactive "p") (message "hello %s" name))
调用了interactive
函数的函数有两个特点:
函数可以通过M-x
调用,也就是说函数是一个命令了
传给interactive
的参数,告诉emacs如何给这个函数传递参数
第二点有这些常见的用法:
(interactive)
,命令不需要参数
(interactive "n")
,命令需要一个数字,提示字符串可以跟在n后面
(interactive "s")
,命令需要一个字符串,提示字符串可以跟在s后面
(interactive "r")
,命令需要2个参数,分别代表选区的开始和结束点
emacs常用函数 编写emacs插件,除了elisp本身外,还需要使用emacs提供的许多API。API是通用称呼,emacs对外的API就是一系列的函数。
光标位置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ;; 获取光标位置。第一个字符的位置为1 (point) ;; 获取选区的开始结束位置 (region-beginning) (region-end) ;; 获取行的开始结束位置 (line-beginning-position) (line-end-position) ;; 获取buffer第一个字符和最后一个字符的位置 (point-min) (point-max)
移动光标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ;; 移动光标到指定位置 (goto-char 392) ;; 移动几个字符 (forward-char n) (backward-char n) ;; 移动到目标字符串的位置 (search-forward myStr) ; end of myStr (search-backward myStr) ; beginning of myStr ;; 移动到目标字符串(使用正则表达式) (re-search-forward myRegex) (re-search-backward myRegex) ;; 移动光标到第一个非a-z字符 (skip-chars-forward "a-z") (skip-chars-backward "a-z")
删除、插入、修改文本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ;; 删除9个字符 (delete-char 9) ;; 删除选区中的字符 (delete-region myStartPos myEndPos) ;; 插入字符 (insert "i ♥ cats") ;; 获取选区中的字符 (setq myStr (buffer-substring myStartPos myEndPos)) ;; 选区中的字符改为大写 (capitalize-region myStartPos myEndPos)
字符串相关 1 2 3 4 5 6 7 8 ;; 获取字符串长度 (length "abc") ; returns 3 ;; 获取子串 (substring myStr startIndex endIndex) ;; 使用正则表达式修改字符串中的文本 (replace-regexp-in-string myRegex myReplacement myStr)
buffer相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ;; 获取buffer的名字 (buffer-name) ;; buffer打开的文件的名字 (buffer-file-name) ;; 切换buffer (set-buffer myBufferName) ;; 保存buffer (save-buffer) ;; 关闭buffer (kill-buffer myBufferName) ;; 暂时地指定一个buffer为当前修改的buffer (with-current-buffer myBufferName ;; 执行操作 )
文件相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ;; 打开文件 (find-file myPath) ;; 另存为buffer。关闭原来的buffer,打开另存为的文件到新buffer中 (write-file myPath) ;; 插入文件内容到光标位置 (insert-file-contents myPath) ;; 最近选区内容到文件尾 (append-to-file myStartPos myEndPos myPath) ;; 重命名文件 (rename-file fileName newName) ;; 复制文件 (copy-file oldName newName) ;; 删除文件 (delete-file fileName) ;; 获取文件所在的目录 (file-name-directory myFullPath) ;; 获取路径文件名部分 (file-name-nondirectory myFullPath) ;; 获取扩展名部分 (file-name-extension myFileName) ;; 获取非扩展名部分 (file-name-sans-extension myFileName)
查看函数、变量文档 C-h f
describe-function
查看函数文档C-h v
describe-variable
查看变量文档
C-h a
apropos-command
查看命令说明apropos-variable
查看变量名说明?apropos-value
查看变量值说明?
elisp-index-search
emacs-index-search
在elisp、emacs文档中搜索
elisp编程的一些配置 默认的Lisp Interaction
不会高亮匹配括号。可以开启(show-paren-mode 1)
来高亮匹配的括号。
1 2 3 4 5 (setq show-paren-style 'parenthesis) ; 高亮括号 (setq show-paren-style 'expression) ; 高亮真个s表达式 (setq show-paren-style 'mixed) ; 如果有括号高亮括号,否则高亮整个表达式
【TODO】elisp中的特殊形式 '
#'
=>(function )
elisp - Emacs Lisp map over a list of function names and call them all with the same arg - Stack Overflow
参考文章