Programming in Emacs Lisp笔记(八)剪切和存储文本
- 博客分类:
- 技术杂绘
Programming in Emacs Lisp笔记(八)剪切和存储文本
2010年06月21日
剪切和存储文本
当使用'kill'命令剪切文本时,Emacs将它存储到一个列表中,可以用'yank'命令重新获取到。
存储文本到列表
当文本被剪切出缓冲区时,它将被存储到一个list中。文本块连续的存储在list中,这个列表看如下面的形式: 函数cons可以添加文本块到list,如:
(cons "another piece" '("a piece of text" "previous piece"))执行上面的语句,回显区将显示
("another piece" "a piece of text" "previous piece")使用car和nthcdr函数,可以获取到list中任意的一个文本块。。例:
(car (nthcdr 1 '("another piece" "a piece of text" "previous piece"))) => "a piece of text"当然,Emacs中实际处理这些时更复杂一些。Emacs中编写的剪切函数能猜想出你需要的是list的哪个元素。
包含这些文本块的list被称作kill ring。
zap-to-char
完整的zap-to-char实现
这个函数将移除光标和指定的字符之间的文本。被移除的文本被放入kill ring中,可以用C-y(yank)获取到。如果命令带了数字前缀参数n(C-u),它将移除当前光标位置至遇到的第n个字符之间的文本。
如果指定的字符不存在,zap-to-char将显示"Search failed"。
为了决定要移除多少文本,zap-to-char使用了search函数。搜索在文本处理代码中使用得非常广泛。
下面是zap-to-char在Emacs 19中的完整代码:
(defun zap-to-char (arg char) ; version 19 implementation "Kill up to and including ARG'th occurrence of CHAR.Goes backward if ARG is negative; error if CHAR not found." (interactive "*p\ncZap to char: ") (kill-region (point) (progn (search-forward (char-to-string char) nil nil arg) (point)))interactive语句
zap-to-char的interactive语句如下:
(interactive "*p\ncZap to char: ")引号中的部分"*p\ncZap to char: ",指定了3个不同的东西。第一,星号,如果当前缓冲区是只读缓冲区将产生一个错误信息。这意味着如果将zap-to-char用于只读缓冲将得到错误信息"Buffer is read-only"。
在Emacs21的实现中没有包含星号。函数与Emacs19中一样能工作,但在只读缓冲区中它不会移除文本,它将复制文本并将文本放到kill ring中。在这种情况下,两个版本中都将显示错误信息。
在Emacs19中的实现也能从只读缓冲区中复制文本,这只是interactive的一个Bug。interactive的文档中说明了,星号将阻止zap-to-char函数对只读缓冲区做任何操作,这个函数不应该复制文本到kill ring中。
在Emacs21中interactive的实现是正确的。因此星号不得不被移除。如果你在这个这个函数的定义中插入了星号,并重新执行函数定义,下次你再在只读缓冲区上运行zap-to-char时,将不能再复制文本到kill ring里。
从这点来看,两个版本中的zap-to-char是一致的。
"*p\ncZap to char: "中的第二个部分是p。这个部分与下一部分用\n分隔了。p表示参数应该是一个前缀参数'processed prefix',这个参数是用C-u加数字或者M-加数字传递的。如果调用时没有加参数,1将作为默认的参数值。
"*p\ncZap to char: "中的第三个部分是"cZap to char: ",小写的c指定了参数必须是一个字符。c后面的字符串Zap to char: 是提示字符串。
zap-to-char的函数体
zap-to-char函数体包含kill当前光标位置至指定字符之间文本的代码。代码的第一部分如下:
(kill-region (point) ...(point)是光标的当前位置
代码的下一个部分是一个progn语句。progn的body部分由search-forward和point组成。
在学习完search-forward后,很容易懂progn。
search-forward函数
search-forward函数被用于定位字符(zapped-for-character)。如果查找成功, search-forward会将point设置在要查找的目标字符串的最后一个字符的后面。(zap-to-char中目标字符串只有一个字符)如果是 向后查找,则search-forward会将point设置在查找目标字符串第一个字符的前面。查找成功后,search-forward将返回t。
在zap-to-char中,search-forward函数部分如下:
(search-forward (char-to-string char) nil nil arg)search-forward函数包含四个参数:
第一个参数是要查询目标,必须是一个字符串,比如"z"。
传递给zap-to-char是一个字符。Lisp解释器对字符串和字符的处理是不同的。因为search-forward函数查询的是一个字符 串,传递给zap-to-char函数接收到的是一个字符,因此参数必须被转换为字符串,否则search-forward将报错。char-to- string用于处理这种转换。
第二个参数限制查询的范围;它是一个缓冲区位置。在这里,可以查询到缓冲区的结束位置,因此第二个参数为nil。
第三个参数告诉函数如果查询失败该如何做:比如打印错误信息或者返回nil。第三个参数为nil将在查询失败时显示错误信息。
search-forward的第四个参数用于指定重复查询的次数。这个参数是可选,如果没有传递,则默认为1.如果参数是一个负数,查询将向后查询。
使用search-forward语句的模板:
(search-forward "target-string" limit-of-search what-to-do-if-search-fails repeat-count)progn
progn是一个特殊的form。它使传递给它的参数依次被执行,并返回最后一个值。前面部分只是被执行,它们的返回值被丢弃。
progn语句的模板:
(progn body...)zap-to-char中的progn语句做了两件事:将point设置到正确的位置;返回point的位置以便kill-region知道要操作的范围。
progn的第一个参数是search-forward。当search-forward找到了字符串,它会将point设置在查找目标字符串的最 后一个字符的后面。(这里目标字符串只有一个字符长)如果是向后查找,search-forward会将poing设置在查找目标的第一个字符的前面。 point的移动是side effect(单方面的,不影响界面)。
progn的第二个参数是表达式(point)。这个表达式返回point的值,即search-forward设置的那个值。这个值被作为progn语句的返回值将作为kill-region的第二个参数传递给kill-region函数。
zap-to-char的总结
前面了解了search-forward和progn是如何工作的,我们可以看到整个zap-to-char函数是如何工作的。
kill-region的第一个参数是执行zap-to-char命令时的光标位置。在progn的内部,查找函数将poing移动到要查找目标 (zapped-to-character)的后面。kill-region函数将这两个point中的第一个作为操作区域(region)的开始位置, 第二个参数作为结束位置,然后移除这个区域。
progn是必需的,因为kill-region命令需要两个参数;如果把search-forward和point语句直接作为kill-region的参数将报错。progn语句是一个单独的参数,它的返回值将作为传递给kill-region的第二个参数。
kill-region
zap-to-char函数使用了kill-region函数。函数将从一个region中clip文本到kill ring中。
在Emacs 21中这个函数使用了condition-case和copy-region-as-kill,这两个函数都将在后面解释,confition-case是一个特别重要的form。
实际上,kill-region函数调用了condition-case,它需要3个参数。第一个参数不做什么,第二个参数包含了正常工作时需要执行的代码。第三个参数包含了出错时需要执行的代码。
完整的kill-region定义
下面将介绍condition-case。首先来看kill-region的完整定义:
(defun kill-region (beg end) "Kill between point and mark.The text is deleted but saved in the kill ring." (interactive "r") ;; 1. `condition-case' takes three arguments. ;; If the first argument is nil, as it is here, ;; information about the error signal is not ;; stored for use by another function. (condition-case nil ;; 2. The second argument to `condition-case' ;; tells the Lisp interpreter what to do when all goes well. ;; The `delete-and-extract-region' function usually does the ;; work. If the beginning and ending of the region are both ;; the same, then the variable `string' will be empty, or nil (let ((string (delete-and-extract-region beg end))) ;; `when' is an `if' clause that cannot take an `else-part'. ;; Emacs normally sets the value of `last-command' to the ;; previous command. ;; `kill-append' concatenates the new string and the old. ;; `kill-new' inserts text into a new item in the kill ring. (when string (if (eq last-command 'kill-region) ;; if true, prepend string (kill-append string (Read only text copied to kill ring") ;; or else, signal an error if the buffer is read-only; (barf-if-buffer-read-only) ;; and, in any case, signal that the text is read-only. (signal 'text-read-only (list (current-buffer)))))))condition-case
前面说过,当Emacs Lisp解释器在执行语句发生错误时,它将提供帮助信息,这被称为"signaling a error"。通常,程序将停止执行并显示错误信息。
然而在一些复杂的情况下。程序不应该在出错的时候只是简单的停止程序执行。在kill-region函数中,一个典型的错误是,如果在只读缓冲区中 删除文本时,文本将不会被删除。因此kill-region函数包含了处理这种情况的代码。这些代码在kill-region函数中condition- case语句的内部。
condition-case的模板如下:
(condition-case var bodyform error-handler...)如果没有发生错误,解释器将执行bodyform语句。
错误发生时,函数将产生错误信息,定义一个或者多个错误条件名称(condition name)。
condition-case的第三个参数是一个错误处理器。一个错误处理器包含了两个部分,一个condition-name和一个body。如 果错误处理器的condition-name与发生错误时的condition-name匹配,错误处理器的body部分将执行。
错误处理器中的错误条件名称(condition-name)可以是一个单一的condition name也可以是包含多个condition name的list。
condition-case语句可以包含一个或多个错误处理器。当错误发生时,第一个被匹配的处理器被执行。
最后,condition-case语句的第一个参数var,有时被绑定到包含错误信息的变量上。如果它为nil,比如在kill-region中,错误消息将被丢弃。
简单来说,在kill-region函数中,condition-case的工作如下:
If no errors, run only this code but, if errors, run this other code.delete-and-extract-region
一个condition-case语句有二个部分,一个是正常时执行的,但它有可能会产生错误。另一个部分用于出错时执行。
先来看kill-region中正常运行的代码:
(let ((string (delete-and-extract-region beg end))) (when string (if (eq last-command 'kill-region) (kill-append string (不能移除时,它给出错误信号)
这里的let语句将delete-and-extract-region的返回值赋给局部变量string中。这也就是从缓冲区中删除的文本。
如果变量string指向了文本,那些文本就被添加到kill ring,如果变量值为nil则表示没有文本被删除。
这里使用了when来检查变量string是否指向了文本块。when语句是程序员的一种简便写法。when语句是没有else部分的if语句。可以把when理解为if。
技术上来说,when是一个Lisp宏。Lisp宏允许你定义新的控制结构和其它语言功能。它告诉解释器如何计算另一个Lisp语句的值,并返回计算的结果。这里的'另一个表达式'就是一个if表达式。C语言里也提供了宏。但这是不同的,但它们同样很有用。
如果string变量有内容,另一个条件表达式被执行。这是一个包含了then部分和else部分的if语句。
(if (eq last-command 'kill-region) (kill-append string (不能恢复。
与其它代码不同,delete-and-extract-region不是用Emacs Lisp编写的;它是用C编写的,这也是Emacs的一个基础系统。
与其它Emacs原生函数一样,delete-and-extract=region是C宏,宏是一个代码模板。完整宏如下:
DEFUN ("delete-and-extract-region", Fdelete_and_extract_region, Sdelete_and_extract_region, 2, 2, 0, "Delete the text between START and END and return it.") (start, end) Lisp_Object start, end;{ validate_region (&start, &end); return del_range_1 (XINT (start), XINT (end), 1, 1);}DEFUN与Lisp中的defun是同样的用途。DEFUN后面括号中有七个部分:
第一个部分给出了Lisp函数的名称,delete-and-extract-region
第二部分是C函数的名称,Fdelete_and_extract_region。习惯上以F开头。因为C不能在函数名中使用连字符,因此用下划线替代了。
第三个部分是记录了供函数内部使用的信息的C常量结构。它的名称与C函数名一致但它以S开头。
第四和第五个部分指定了最小和最大的参数个数。这个函数需要2个参数。
第六部分与Lisp编写的函数中的交互式语句类似:一个字符后跟着可选的提示信息。两者不同之处在于Lisp没有参数时不需要写参数。在这个宏里需要写成0(null string)。
第七个部分是文档字符串与Lisp编写的函数中的相同。不同之处在于换行时,需要在\n后面添加一个反斜线并添加回车。
因此,goto-char的文档字符串的前两行如下:
"Set point to POSITION, a number or marker.\n\ Beginning of buffer is position (point-min), end is (point-max).在C宏中,紧接在后面是正式的参数,和参数类型语句,接下来就是宏的'body'部分。delete-and-extract-region的'body'包含了两行: validate_region (&start, &end);return del_range_1 (XINT (start), XINT (end), 1, 1);第一个函数validate_region检查传递的区域起始位置和结束位置是否是在规定的范围内,检查参数类型是否正确。第二个函数del_range_1,执行删除文本的操作。
del_range_1是一个复杂的函数我们不深入研究。它修改缓冲区并执行其它操作。
传递给del_range的两个参数XINT (start) and XINT (end)值得研究一下。
C语言中,start和end是标记了被删除区域的开始位置和结束位置的两个整数。
早期版本的Emacs中,这两个数字是32bits长,但这个代码运行比较慢。三个bit被用于指定类型信息,四个bit被用于处理内存;其它bits被作为'content'。
XINT是一个C宏它从bits集合中解析出相关的数字;4个bits被丢弃。
delete-and-extract-region命令看起来如下:
del_range_1 (XINT (start), XINT (end), 1, 1);它删除start和end之间的region。
从这点来看Emacs Lisp很简单;它隐藏了大量复杂的工作。
使用用defvar初始化变量
与delete-and-extract-region函数不同,copy-region-as-kill函数是用 Emacs Lisp编写的。它内部有两个函数kill-append和kill-new,复制缓冲区区域中的信息到变量kill-ring中。这节讨论kill- ring变量是如何被defvar创建和初始化的。
在Emacs Lisp中kill-ring之类的变量是用defvar创建和初始化的。这个名称来源于"define variable"。
defvar与setq设置变量类似。与setq不同的两点:第一,它只给未赋值的变量赋值,如果变量已经有值,defvar将不会覆盖已经存在的值。第二,defvar有一个文档字符串。
(另一个特别的form是defcustom,被设计为可以让用户自定义。它比defvar有更多的功能。)
查看变量的当前值
可以使用describe-variable函数查看任何变量的当前值,通常可以用C-h v来调用。比如可以C-h v然后输入kill-ring将看到 当前kill ring的值,同时也能看到kill-ring的文档字符串:
Documentation:List of killed text sequences.Since the kill ring is supposed to interact nicely with cut-and-pastefacilities offered by window systems, use of this variable shouldinteract nicely with `interprogram-cut-function' and`interprogram-paste-function'. The functions `kill-new',`kill-append', and `current-kill' are supposed to implement thisinteraction; you may want to use them instead of manipulating the killring directly.kill ring是使用defvar按下面的方法定义的:
(defvar kill-ring nil "List of killed text sequences....")这个变量定义中,变量初始化为nil。这意味着如果没有保存任何东西,使用yank时将不会获取到任何信息。文档字符串的写法与使用defun时的文档字 符串是一样的,文档字符串的第一行必须是一个完整的语句,因为一些命令,比如apropos只打印文档字符串的第一行。后面的行不应该使用缩进;否则如果 用C-h v(describe-variable)查看时将会混乱。
defvar时使用星号
以前,Emacs使用defvar来定义希望被用户修改的变量和不希望被用户修改的变量。尽管你可以用defvar定义自定义变量,但是请使用defcustom来代替。
当使用defvar设定变量时,可以在文档字符串的第一个位置添加*号来来区分变量是否为可以设值的变量。比如:
(defvar shell-command-default-error-buffer nil "*Buffer name for `shell-command' ... error output.... ")这表示你可以使用edit-options命令临时修改shell-command-default-error-buffer的值。
edit-options设置的值只在当前编辑会话中有用。新值并不会被保存。每次Emacs启动时它将读取原始值,除非你在.emacs文件中设定它。
copy-region-as-kill
这个函数从缓冲区中复制区域中的内容(使用kill-append或kill-new)并保存到kill-ring上。
如果在调用kill-region后立即调用copy-region-as-kill,Emacs会将新的文本追加到前一个复制的文本中。这意味着 你使用yank时将得前面两次操作的所有文本。另一方面,如果在copy-region-as-kill之前执行了一些命令,则函数复制的文本块将不会放 在一起。
完整的copy-region-as-kill函数定义
下面是Emacs 21中copy-region-as-kill函数定义:
(defun copy-region-as-kill (beg end) "Save the region as if killed, but don't kill it.In Transient Mark mode, deactivate the mark.If `interprogram-cut-function' is non-nil, also savethe text for a window system cut and paste." (interactive "r") (if (eq last-command 'kill-region) (kill-append (buffer-substring beg end) (出来的值上。在这里是表达式 ( (length kill-ring) kill-ring-max) (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))) (setq kill-ring-yank-pointer kill-ring) (if interprogram-cut-function (funcall interprogram-cut-function string (not replace))))先看下面的部分:
(if (and replace kill-ring) ;; then (setcar kill-ring string) ;; else (setq kill-ring (cons string kill-ring)) (if (> (length kill-ring) kill-ring-max) ;; avoid overly long kill ring (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))) (setq kill-ring-yank-pointer kill-ring) (if interprogram-cut-function (funcall interprogram-cut-function string (not replace))))条件测试(and replace kill-ring),如果两个kill ring中有内容,并且replace变量为true则返回true。
kill-append函数将replace设置为true;然后当kill ring至少有一个元素时,setcar语句被执行:
(setcar kill-ring string)setcar函数将kill-ring的第一个元素修改为string的值。它替换了原来的元素。
如果kill ring为空,或者replace为false,则条件语句的else部分将执行:
(setq kill-ring (cons string kill-ring))(if (> (length kill-ring) kill-ring-max) (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))语句先通过在原来的kill ring前添加新元素string,而构造了一个新的kill ring。然后执行了第二个if子句。第二个if子名防止了kill ring增长过大。
依次来看这两个语句。
setq的这行将string添加到旧的kill ring组成的新list重新设置给kill-ring。
第二个if子名,防止了kill ring增长得过长。
(if (> (length kill-ring) kill-ring-max) (setcdr (nthcdr (1- kill-ring-max) kill-ring) nil))这段代码检查kill ring的长度是否已经超过了允许的最大长度--kill-ring-max(默认为60)。如果kill ring过长,则将kill ring的最后一个元素设置为nil。执行这个操作使用了两个函数:nthcdr和setcdr。
setcdr设置list的CDR部分,setcar设置list的CAR部分。在这里,setcdr不会设置kill ring的CDR部分;nthcdr函数限制了设置CDR的位置。
例:
(setq trees '(maple oak pine birch)) => (maple oak pine birch)(setcdr (nthcdr 2 trees) nil) => niltrees => (maple oak pine)setcdr返回值为nil,是因为它设置的CDR是nil。
kill-new函数中的下一行语句是:
(setq kill-ring-yank-pointer kill-ring)kill-ring-yank-pointer也是一个全局变量,它被设置为kill-ring。
尽管kill-ring-yank-pointer被称为pointer,实际上却是kill ring变量。但选用名字是为了帮助人们懂得这个变量起的作用。这个变量用于yank和yank-pop等函数。
现在,回到函数的最前面的两行:
(and (fboundp 'menu-bar-update-yank-menu) (menu-bar-update-yank-menu string (and replace (car kill-ring))))这个语句第一个元素是函数and。
and将依次对每个参数求值只到某个参数返回值为nil,这种情况下and语句将返回nil;如果没有参数返回值为nil,返回值将是最后一个参数 的值。(这种情况下返回值不会为nil,在Emacs Lisp里可以作为true)。换言之,and语句只有在所有参数都返回true的情况下才返回true。
在这里,语句测试了menu-bar-update-yank-menu是否是一个函数,如果是则调用它。如果测试的参数符号是一个函数定义而不是'is not void',则fboundp返回true,如果函数未定义则我们将得到错误信息。
这个and和if语句效果如下:
if the-menu-bar-function-exists then execute-itmenu-bar-update-yank-menu函数允许用户使用'Select and Paste'菜单操作,并且可以在菜单上看到文本块。
最后一个语句kill-new函数添加新的文本到窗口系统中,以便在不同的程序中进行复制粘贴操作。比如:在XWindow系统中x-select-text函数将文本存储在X系统操作的内存中,你可以在另一个程序中粘贴。
语句结构如下:
(if interprogram-cut-function (funcall interprogram-cut-function string (not replace))))如果interprogram-cut-function存在,则Emacs执行funcall,它将第一个参数作为函数,并将其它参数传递给这个函数。
回顾
car
cdr
car返回list的第一个元素;cdr返回list中从第二个元素开始的list。
例:
(car '(1 2 3 4 5 6 7)) => 1(cdr '(1 2 3 4 5 6 7)) => (2 3 4 5 6 7)cons
cons将第一个参数添加到第二个参数前面。
例:
(cons 1 '(2 3 4)) => (1 2 3 4)nthcdr
返回对list求'n'次CDR的值。
例:
(nthcdr 3 '(1 2 3 4 5 6 7)) => (4 5 6 7)setcar
setcdr
setcar修改list中的第一个元素;setcdr修改list中第二个元素开始的list。
例:
(setq triple '(1 2 3))(setcar triple '37)triple => (37 2 3)(setcdr triple '("foo" "bar"))triple => (37 "foo" "bar")progn
依次执行各个参数并返回最后一个参数的值。 例: (progn 1 2 3 4) => 4save-restriction
记录当前缓冲区的任何narrowing,在执行完它的参数后,恢复narrowing。
search-forward
查找字符串,如果找到则将point设置到那个位置。
它接收4个参数:
要查找的字符串
可选参数,是一个缓冲区位置,它用于限制查询范围
可选参数,查询失败执行的代码,返回nil或者显示错误信息
查询的次数,如果为负数则向前查找
kill-region
delete-region
copy-region-as-kill
kill-region剪切point和mark间的文本到kill ring上,可以用yanking恢复。
delete-and-extract-region移除point和mark间的文本并丢弃。不能恢复。
copy-region-as-kill复制point和mark间的文本到kill ring,可以用yanking恢复。这个函数并不移除原来的文本。
发表评论
-
J2ME游戏开发笔记整编版
2012-01-20 02:47 831J2ME游戏开发笔记整编版 ... -
【详解】如何编写Linux下Nand Flash驱动 1/2
2012-01-20 02:47 1368【详解】如何编写Linux ... -
EXT2文件系统、虚拟文件系统、proc文件系统
2012-01-20 02:47 763EXT2文件系统、虚拟文件系统、proc文件系统 2010年 ... -
编程珠玑番外篇-K. Plan 9 的故事(修订版)
2012-01-20 02:47 815编程珠玑番外篇-K. Plan 9 ... -
映像劫持
2012-01-17 03:10 745映像劫持 2011年05月20日 ... -
C语言期末考试题
2012-01-17 03:10 629C语言期末考试题 2010年06月17日 一 ... -
选择题
2012-01-17 03:10 786选择题 2011年10月20日 1、根据加工零件图样选定 ... -
c语言(1)考试题目
2012-01-17 03:10 950c语言(1)考试题目 2010年06月17日 一、 ... -
C语言3
2012-01-17 03:10 836C语言3 2011年01月17日 24.有以下程序 ... -
VB钓鱼
2012-01-16 01:42 781VB钓鱼 2010年04月08日 Private Sub ... -
VB 获取 Internet Explorer_Server 里面的内容
2012-01-16 01:42 1760VB 获取 Internet Explorer_Server ... -
VB操作网页元素
2012-01-16 01:42 979VB操作网页元素 2010年01月23日 用webbro ... -
IE浏览器交互
2012-01-16 01:41 751IE浏览器交互 2010年12月11日 VB与――自动 ... -
VB读取网站超链接!
2012-01-16 01:41 795VB读取网站超链接! 2011年01月23日 Priva ... -
java JNDI
2012-01-11 01:31 642java JNDI 2011年09月01日 数据库连接 ... -
oracle一些基本的sql语句-alex-iteye技术网站
2012-01-11 01:30 545oracle一些基本的sql语句-alex-iteye技术网站 ... -
ExtJs combobox 自定义过滤-模糊过滤
2012-01-11 01:30 1095ExtJs combobox 自定义过滤-模糊过滤 2011 ... -
mysql导入导出命令-java 初学者-iteye技术网站
2012-01-11 01:30 520mysql导入导出命令-java 初学者-iteye技术网站 ... -
List-LinkedList- 源代码 研究
2012-01-11 01:30 544List-LinkedList- 源代码 研究 2011年0 ...
相关推荐
Programming in Emacs Lisp英文版
An Introduction to Programming in Emacs Lisp Second Edition 经典中的经典!
Robert Chassell:An Introduction to Programming in Emacs Lisp
emacs官网上的那个字体太不好了,这是用源文件重编的,看起来不错
An Introduction to Programming in Emacs Lisp, 3rd Edition
An Introduction to Programming in Emacs Lisp [3.10].chm
Programming in Emacs Lisp: An Introduction (美)Robert J.Chassell 著 毛文涛、吕芳 译 洪峰 审校 本书的作者罗伯特·卡塞尔是自由软件基金会的合创人之一,也是理查德·斯托曼博士青年时期结交的挚友,他...
GNU Emacs Lisp编程入门(清晰版) 英文名:An Introduction to Programming in Emacs Lisp
GNU emacs Lisp manual This is a very interesting text, useful to write and program in several languages. Emacs is a editor made in Lisp, a artificial intelligence language.
Atom-language-emacs-lisp.zip,emacs lisp和yasnippet支持atom和github。Emacs Lisp支持,atom是一个用web技术构建的开源文本编辑器。
GNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvu
gnu emacs lisp 编程指南, 很好的一本书
emacs lisp 函数手册,emacs 24.3版本
GNU Emacs Lisp Reference Manual For Emacs Version 22.1 Revision 2.9, April 2007
GNU Emacs Lisp Reference Manual 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开
学习 EMACS 和 LISP 极好的入门教材,这可是好东西
强大的emacs lisp编程 不仅仅适用于初学者
emacs-lisp-intro emacs-lisp-intro