精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-06-27
一些更复杂的函数copy-to-buffer的函数定义这个函数拷贝文本到缓冲区,但它不是追加到第二个缓冲区,而是替换第二个缓冲区之前的文本。copy-to-buffer函数与append-to-buffer代码很类似,但它使用了erase-buffer和二个save-excursion。 该函数的函数体如下: ...代码与append-to-buffer类似:不同处在于,改变buffer后append-to-buffer添加文本到缓冲区;而copy-to- buffer函数先删除缓冲区的内容。在删除之前的缓冲区的内容后,第二次使用了save-excursion,并且插入了新的文本。 为什么需要执行save-excursion两次? 单独提取copy-to-buffer函数体如下: (let (bind-oldbuf-to-value-of-current-buffer) 第一个save-excursion让Emacs返回被复制文本的缓冲区。很清楚,这与append-to-buffer函数中的使用是一致的。为 什么要使用第二个save-excursion呢?原因在于insert-buffer-substring总是将point设置在被插入的区块 (region)的结束位置。第二个save-excursion将使用Emacs将point设置在被插入区块的开始位置。多数情况下,用户喜欢看到 point停留在被插入文本的开始位置。(copy-to-buffer函数将返回用户最初所在的缓冲区,当用户切换到拷贝的目标缓冲区时,point停 留在缓冲区开始的位置)。 insert-buffer的函数定义与append-to-buffer和copy-to-buffer相反,这个命令拷贝另一个缓冲区到当前缓冲区。 insert-buffer的代码(defun insert-buffer (buffer) insert-buffer中的交互insert-buffer中的interactive有两个部分,*号和bInsert buffer: 只读缓冲区星号用于只读缓冲区。如果insert-buffer是在一个只读缓冲区上被调用,提示信息将在回显区显示提示不允许插入到当前的缓冲区。星号不需要使用\n与下一个参数分隔。 交互表达式b交互表达式的第二个参数是小写b开头的(append-to-buffer中是大写的B)。小写b告诉Lisp解释器,insert-buffer 需要一个已存在的缓冲区或者已存在的缓冲区名称作为参数。(大写的B可以使用一个不存在的缓冲区)Emacs将提示输入缓冲区名称,并提供了默认的缓冲 区,输入时可以使用自动完成功能。如果缓冲区不存在,将给出"No match"的提示。 insert-buffer函数体insert-buffer函数有两个主要部分:or语句和let语句。or语句用于确保参数buffer参数不仅仅只是被绑定到缓冲区的名字上。let语句包含复制其它缓冲区到当前缓冲区的代码。 (defun insert-buffer (buffer)要明白or如何确保参数buffer不只是被绑定到缓冲区名称上,先要清楚or函数。 在insert-buffer用if替代or主要工作在于确保buffer变量值是一个缓冲区,而不是缓冲区的名字。如果变量值是名字,则需要获取对对应的缓冲区。 通过if来实现:如果没有获取到buffer就获取它。 这里使用了bufferp函数,这个函数检查参数是否为一个缓冲区(或者缓冲区的名字),我们可以如下编码: (if (not (bufferp buffer)) ; if-part前面说过bufferp中的字符p是一个约定的函数描述,它意味着函数用于决定某些属性为true或false。这里bufferp就是用于检查参数是否为一个缓冲区。 not函数用于取逻辑值的反值。 当buffer参数不是一个缓冲区但它是一个缓冲区名称时,true-or-false-test返回true。这时(set q buffer (get-buffer buffer))被执行。语句使用get-buffer函数获取缓冲区名称所对应的缓冲区。setq将buffer绑定到缓冲区上。 函数体中的orinsert-buffer函数中使用or语句的目的在于确保buffer被绑定到缓冲区。上一节用if实现了这个功能。但在insert-buffer函数中实际使用的却是or函数。 or函数可以接收任何意数量的参数。它依次对每个参数求值并返回第一个结果不为nil的值。or并不会对第一个返回值不为nil的参数的后面的参数求值。 or语句如下: (or (bufferp buffer)该语句中or的第一个参数为(bufferp buffer)。如果buffer参数是一个缓冲区则返回true(一个非nil值)。在or语句中,这种情况下or将返回true,并且不执行后面的语句。 如果(bufferp buffer)返回值为nil,即buffer是一个缓冲区的名字,Lisp解释器将执行or语句的下一个元素:(setq buffer (get-buffer buffer))。这个语句将返回一个非nil值,这个值为绑定到buffer变量上的缓冲区而不是缓冲区的名字。 使用or的情况: (or (holding-on-to-guest) (find-and-take-arm-of-guest)) insert-buffer中的let语句确保了buffer变量绑定到缓冲区后,insert-buffer函数中接下来是一个let语句。它设置了3个局部变量start、end和newmark并初始化为nil。这些变量是let语句中的临时变量。 let语句体包含了两个save-excursion语句。内部的那个save-excursion如下: (save-excursion(set-buffer buffer)将将当前缓冲区设置为将要复制文本的缓冲区。在那个缓冲区中将start和end分别设置为缓冲区开始位置和结束位置。这里可以看到setq可以在一个语句中设置多个变量。第一个参数值设置为第二个参数,第三个参数值为第四个参数。 外部的那个save-excursion表达式结构如下: (save-excursion insert-buffer-substring函数从原缓冲区中把start和end所定义的区域中的文本拷贝到buffer中。第二个缓冲区所 有内容都处于start和end之间,因此整个缓冲区都将被拷贝到当前编辑的缓冲区中。这时,point位于被插入的文本的结束位置,被保存到 newmark变量中。 在执行外部的save-excursion语句后,point和mark将回到原来的位置。 然而,合适的mark位置应该被设置在被插入的文本块的结束位置,而point应当被设置在这个文本块的开始位置。newmark记录了被插入文本 的结束位置。let语句的最后一行(push-mark newmark)语句将mark设置到了那个位置。(前一个mark仍然可以访问,它被保存在mark ring里面,可以用C-u C-<spc>回到那个位置)。同时,point被设置到被插入文本的开始位置,这也是函数调用前point所在的位置。</spc> 完整的beginning-of-buffer函数的定义前面讨论过"简化版的beginning-of-buffer函数定义"。在那个版本中,调用的时候没有传递参数。 Emacs中的beginning-of-buffer将光标移到缓冲区开始位置,并将mark设置在之前光标所在位置。调用的时候可以传递1-10之间 的数字给这个命令,函数将把参数当作移动的百分比:整个缓冲区当作10份,C-u 7 M-<将跳转到整个缓冲区70%的位置。M-<将中跳转到缓冲区的开始位置。如果传递的参数大于10,则将移动到缓冲区的结束位置。 beginning-of-buffer的参数是可选的,可以不带参数调用。 可选参数在调用需要参数的函数时,如果没有设置参数Lisp解释器将报错:Wrong number of arguments。 但Lisp提供了可选参数的功能:用&optional(&是这个关键字的一部分)关键字告诉解释器参数是可选的,如果参数跟在&optional的后面,则在调用函数时可以不传递这个参数 beginning-of-buffer函数定义的第一行: (defun beginning-of-buffer (&optional arg)整个函数看起来如下: (defun beginning-of-buffer (&optional arg) 整个函数与simplified-beginning-of-buffer函数类似,除了interactive语句用了"P"参数和goto-char函数跟了一个用于在传递了参数时计算光标位置if-then-else语句。 "P" interactive语句告诉Emacs传递一个前缀参数,这个参数来自于按键前输入的数字。或者输入C-u时输入的数字。(如果不输入数字,C-u缺省为4) if语句部分比较简单:如果参数arg的值不为nil,即调用beginning-of-buffer时有带参数,则true-or-false- test返回true,if语句的then部分将被执行。如果beginning-of-buffer调用时没有带参数则if语句将被执行。else部分 (goto-char (point-min))被执行。 带参数执行beginning-of-buffer当带参数执行时,用于计算传递给goto-char的参数值的语句被执行。这个语句初看起来比较复杂。它内部包含了一个if语句和更多的数学计算。 (if (> (buffer-size) 10000) 解开beginning-of-buffer解开上面的条件语句如下: (if (buffer-is-largeif语句检查缓冲区的大小。这样做主要是由于在由的Emacs 18版本中计算出来的数字不允许大于8百万,Emacs怕缓冲区太大,后面的计算结果超过上限而溢出。在Emacs 21中使用大数字,但这个代码没被改动。 缓冲区很大时的情况在beginning-of-buffer中,内部的if语句为了检查缓冲区是否大于1000个字符使用了>函数和buffer-size函数。 (if (> (buffer-size) 10000)如果超过了if语句的then部分将执行: (*语句使用*函数将两个参数相乘。 第一个参数(prefix-number-value arg)。当使用"P"作为interactive时,传递给函数的参数值是一个"raw prefix argument",不是一个数字(是一个包含了一个数字的列表)。为了执行数字运行,需要通过prefix-number-value来做转换。 第二个参数是(/ (buffer-size) 10)。这个语句将数值与十相除。这计算出了缓冲区中1/10有多少个字符。 在整个相乘的语句如下: (* numeric-value-of-prefix-arg如果传递的参数是7,计算出的位置就是缓冲区70%的位置。 缓冲区很大的时候,goto-char语句的情况: (goto-char (* (prefix-numeric-value arg) 缓冲区较小时的情况如果缓冲区包含的字符数量小于10000,计算上有些不同。也许你会认为这没有必要,因为第一个计算方式(大于10000时的情况)也能工作。然而在小型缓冲区中,第一种方法不能将光标放在需要位置;第二种方法则工作得好一些: (/ (+ 10 (* (buffer-size) (prefix-numeric-value arg))) 10))格式化后看得更清楚一些: (/看最内部的括号(prefix-numberic-value arg),它将raw argument转换为数字。然后将数字与缓冲区大小相乘。 <src lang="lisp"></src> 这个操作将得到一个大于缓冲区几倍的数字。然后用这个数字加上10最后再除以10得到一个大于百分比位置的值。 这个结果被传递给goto-char将光标移到那个点。 beginning-of-buffer的完整代码(defun beginning-of-buffer (&optional arg) 代码中的文档字符串中使用了一个语句: \(goto-char (point-min))语句第一个括号前的\告诉Lisp解释器应该打印这个表达式而不是对它求值。 beginning-of-buffer的最后一行代码:如果执行命令时带了参数,则移动point到下一行的起始位置。 (if arg (forward-line 1)))这行代码将光标移动到了计算位置的下一行的起始位置。(这行代码并非必要的,只是为了看起来更好) 回顾
依次执行各个参数直到遇到一个返回值不为nil的值。如果没有返回值为nil的参数,则返回nil,否则返回第一个返回值不为nil的值。简单来说就是:返回参数中第一个为true的值。
依次执行各个参数,直到遇到返回值为nil时,返回nil;如果没有nil则返回最后一个参数的值。简单来说就是:如果所有参数都为true就返回true;
这个关键字用于标明函数定义中的参数是可选的参数。意味着调用函数时可以不传递这个参数。
将从(interactive "P")获取到的"raw prefix argument'转换为数字。
将point移到下一行的行首,如果参数大于1,则向下移动多行。如果不能移动那么多行,则forward-line尽量移动到能到达的位置,并返回没有进行操作的多余次数。
删除当前整个缓冲区中的内容。
如果参数是一个缓冲区,则返回t,否则返回nil。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3266 次