论坛首页 综合技术论坛

Programming in Emacs Lisp笔记(四)与缓冲区有关的函数

浏览 3776 次
精华帖 (1) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-26  
部分与缓冲区有关的函数
查找更多信息

可以通过C-h f查看函数的说明,C-h v查看变量的说明,这些说明就是Emacs Lisp代码中的文档字符串。

在20或更高版本以后,可以用describe-function(C-h f)将告诉你函数定义的位置。在文件名上按回车(这个操作是help-follow函数调用)将打开函数定义。

etags:在代码中如果想要查看函数源文件,可以使用find-tags函数跳转到源文件上去。find-tags可以处理多种语法,不限于 Lisp和C,也可以工作于非编程语言如Texinfo文档。在Texinfo文档里调用find-tags将跳转到对应的文件节点。

find-tags函数依赖于标签表'tags tables',它记录了函数、变量和其它信息的位置。

使用M-.调用find-tags函数,然后在提示符后要查找的函数名,比如mark-whole-buffer。Emacs将转到显示该函数源码缓冲区。

符号表'tags table'通常是一个名为TAGS的文件。Emacs源码的TAGS存储在/usr/local/share/emacs目录中。但可以通过M-x visit-tags-table命令指定一个符号表。

通过etags命令可以创建符号表。使用M-x cd命令或C-x d(dired)切换到要建立符号表的目录,然后运行编译命令执行etags *.el命令。例:

M-x compile RET etags *.el RET

文件中的代码通常称为库。通过C-h p可以查看Emacs Lisp的标准库。
简化版的beginning-of-buffer函数定义

beginning-of-buffer命令将把光标移动到缓冲区起始位置,并把mark设置在前一个位置。通常被绑定到M-<上。

简化版本的函数具有与标准库版本类似的功能,但不包含完整的功能。

设想一下该函数的定义应包括:包含能让用户交互的表达式,比如按M-x beginning-of-buffer或按键C-<;必须包含能在原位置设置mark的代码;必须包含让光标移动到缓冲区起始位置的代码。

简化版本的代码:

(defun simplified-beginning-of-buffer ()
  "Move point to the beginning of the buffer;
leave mark at previous position."
  (interactive)
  (push-mark)
  (goto-char (point-min)))

与其它函数一样,这个函数也包含了defun需要的五个部分:

   1. 函数名,这里使用的:simplified-beginning-of-buffer。
   2. 参数列表:这里是一个空列表()。
   3. 文档字符串。
   4. 交互表达式。
   5. 函数体。

这个函数定义中,参数列表为空,意味着函数不需要任何参数。(查看完整函数定义时,我们可以看到它可能传递了可选的参数)。

interactive语句告诉Emacs函数允许交互。这个例子中interactive没有参数,因此调用simplified-beginning-of-buffer时也不需要参数。

函数体由两行代码组成:

(push-mark)
(goto-char (point-min))

(push-mark)执行时将在当前光标所在位置设置mark,之前的mark被保存到mark ring上。

(goto-char (point-min))将光标转移到缓冲区起始位置。

在阅读这些代码遇到陌生的函数时,比如goto-char,可以使用describe-function命令,按C-h f然后输入函数名。describe-function将在一个*Help*窗口打印出函数的帮助文档。

调用describe-function时,如果光标停留在函数名上,describe-function默认将把光标所在位置的函数名作为参数。

end-of-buffer的函数定义与beginning-of-buffer类似,只是用(goto-char (point-max))替换(goto-char (point-min))。
mark-whole-buffer的定义

mark-whole-buffer函数标把整个缓冲区标记为区域。把point设置在缓冲区开始位置,mark设置在缓冲区结束位置。通常被绑定到C-x h上。
mark-whole-buffer预览

在Emacs 20中,完整的函数代码如下:

(defun mark-whole-buffer ()
  "Put point at beginning and mark at end of buffer."
  (interactive)
  (push-mark (point))
  (push-mark (point-max))
  (goto-char (point-min)))

与函数定义模板类似:

(defun name-of-function (argument-list)
  "documentation..."
  (interactive-expression...)
  body...)

mark-whole-buffer的函数体

mark-whole-buffer的函数体仅有3行:

(push-mark (point))
(push-mark (point-max))
(goto-char (point-min))

第一行(push-mark (point))与simplified-beginning-of-bufer函数相同,那里写的是(push-mark。两种写法都是告诉解释器在当前光标所在位置设置mark。

下一行(push-mark (point-max))在缓冲区结尾设置mark。设置这个mark后,前一行设置的mark将进入mark ring。这意味着你可以通过按两次C-u C-<SPC>重新回到那个位置。

在Emacs 21里,(push-mark (point-max))看起来更复杂:

(push-mark (point-max) nil t)

这个函数增加了两上参数,第二个参数为nil。这告诉函数,在设置mark后,需要显示消息:'Mark set'。第三个参数t,它告诉push-mark如果当Transient Mark mode是开启状态,则应该将mark设置为活动状态。Transient Mark mode将高亮显示当前活动的区域,它默认是关闭的。

最后一行(goto-char (point-min))与beginning-of-buffer完全一样。这个语句将光标移动到缓冲区开始位置。运行的结果:point被设置到缓冲区开始位置,mark被设置为缓冲区结束位置。整个缓冲区被标记为区域(region)。
append-to-buffer的定义

append-to-buffer函数从当前缓冲区中拷贝选中区域到指定的缓冲区中。
append-to-buffer函数预览

append-to-buffer命令使用insert-buffer-substring函数来拷贝区域。insert-buffer-substring函数将缓冲区的一部分作为字符串(substring)插入到另一个缓冲区。完整的函数代码如下:

(defun append-to-buffer (buffer start end)
  "Append to specified buffer the text of the region.
It is inserted into that buffer before its point.

When calling from a program, give three arguments:
a buffer or the name of one, and two character numbers
specifying the portion of the current buffer to be copied."
  (interactive "BAppend to buffer: \nr")
  (let ((oldbuf (current-buffer)))
    (save-excursion
      (set-buffer (get-buffer-create buffer))
      (insert-buffer-substring oldbuf start end))))

append-to-buffer函数中的交互表达式

append-to-buffer函数需要与用户交互,因此函数使用了interactive表达式。

(interactive "BAppend to buffer: \nr")

这个语句有一个双引号包含的字符串,该字符串被\n分隔为两部分。

第一部分BAppend to bufefer:这里的B告诉Emacs传递一个缓冲区给函数。Emacs将在回显区用B后面的字符串(Append to buffer:)提示用户输入缓冲区名称。然后Emacs将该缓冲区到参数buffer上。

\n分隔了交互表达式中的字符串。\n后的r告诉Emacs将point和mark的值分别绑定到参数buffer后面的两个参数上。
append-to-buffer的函数体

函数体是以let开头的。定义了局部变量,在函数中let将(current-buffer)的返回值绑定到oldbuf上。这个变量用于跟踪当前工作的缓冲区。

单独来看let语句:

(defun append-to-buffer (buffer start end)
  "documentation..."
  (interactive "BAppend to buffer: \nr")
  (let ((variable value))
        body...)

let语句包含三个元素:

   1. 符号let。
   2. 变量列表,列表中的元素是只含有两个元素的列表。
   3. let语句体。

append-to-buffer中的save-excursion

let语句的body部分是一个save-excursion语句。它用于在执行完代码后恢复point,mark和buffer的值。

使用save-excursion函数处理过程与下面的模板类似:

(save-excursion
  first-expression-in-body
  second-expression-in-body
   ...
  last-expression-in-body)

在函数定义中,save-excursion只包含了两个语句:

(set-buffer (get-buffer-create buffer))
(insert-buffer-substring oldbuf start end)

save-excursion依次执行这两条语句,save-excursion函数执行的最后一条语句被作为该次函数调用的返回值。

save-excursion的body部分的第一条语句set-buffer函数用于改变当前缓冲区到append-to-buffer函数调用时第一个参数所指定的缓冲区。(set-buffer变不改当前屏幕显示的内容,只在Lisp程序内部改变当前处理的缓冲区)。第二条语句执行了函数的主要工作。

(get-buffer-create buffer)语句根据名称获取缓冲区,如果缓冲区不存在,就创建一个同名的缓冲区。这意味着可以用append-to-buffer将文本放到一个之前不存在的缓冲区上。

get-buffer-create也使set-buffer可以从错误中恢复:set-buffer需要一个缓冲区才能工作;如果传递给它的缓冲区不存在,Emacs将报错。get-buffer-create将在缓冲区不存在时创建一个,因此set-buffer将总能获取到一个缓冲区。

最后一行append-to-buffer执行追加文本的工作:

(insert-buffer-substring oldbuf start end)

insert-buffer-substring函数从指定的缓冲区中拷贝字符串到当前缓冲区。在这里,传递给inset-buffer- substring的参数值是由let绑定的,并被命名为oldbuf,它是开始执行append-to-buffer时的当前缓冲区(执行命令时屏幕上显示的缓冲区)。

在insert-buffer-substring执行完后,save-excursion将恢复原缓冲区,append-to-buffer工作完成。

append-to-buffer的函数体工作骨架:

(let (bind-oldbuf-to-value-of-current-buffer)
  (save-excursion                       ; Keep track of buffer.
    change-buffer
    insert-substring-from-oldbuf-into-buffer)

  change-back-to-original-buffer-when-finished
let-the-local-meaning-of-oldbuf-disappear-when-finished

总结append-to-buffer的工作方式:它保存当前缓冲区到变量oldbuf。获取或者新建一个缓冲区并让Emacs切换到那个缓冲区(非屏幕上显示的缓冲区)。使用oldbuf变量从旧缓冲区中获取文本区域插入到新缓冲区;然后使用save-excursion函数回到最初的缓冲区。

查看append-to-buffer代码,探究了复杂的函数。它展示了如何使用let和save-excursion,如何从另一个缓冲区回到原来的缓冲区。许多函数定义中以这种方式使用了let、save-excursion和set-buffer。
回顾

    * describe-function
    * describe-variable

打印函数或变量文档字符串。通常被绑定到C-h f和C-h v。

    * find-tag

查找包含函数或变量的源码,并在缓冲区打开,并定位到对应的位置。通常绑定到M-.上。

    * save-excursion

保存当前的point和mark,在传递给save-excursion的参数执行完后恢复这两个值。它也会记录下当前的缓冲区并重新回到这个缓冲区。

    * push-mark

在某个位置设置mark并将之前的mark保存到mark ring里面去。mark是缓冲区中的一个位置,不管缓冲区中的文本添加或者删除,它都将保持它的相对位置。

    * goto-char

设置point到参数指定的位置,参数可以是数字、mark或者返回位置数据的表达式,例如:(point-min).

    * insert-buffer-substring

从传递给它的第一个参数(缓冲区)对应的缓冲区拷贝区域中的文本到当前缓冲区。

    * mark-whole-buffer

把整个缓冲区标记为一个区域。通常绑定到C-x h上。

    * get-buffer-create
    * get-buffer

按名称查找缓冲区,如果缓冲区不存在就创建一个。get-buffer函数在查找不到缓冲区后将返回nil。
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics