[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. Что такое буфер

В этой главе мы детально изучим несколько реальных функций, написанных на Emacs Лиспе. Это называется проходом по (walk-through). Эти функции используются как примеры Лисп кода, и это не придуманные примеры; исключая первую, немного упрощенную функцию, они демонстрируют настоящий код используемый, в GNU Emacs. Вы можете многому научиться изучая эти функции. В последующих главах мы изучим и несколько других функций.

4.1 Поиск дополнительной информации  
4.2 Упрощенное определение beginning-of-buffer  Объяснение goto-char,
                                    point-min, и push-mark.
4.3 Определение mark-whole-buffer  Почти тоже самое как и
                                    beginning-of-buffer.
4.4 Определение append-to-buffer  Использует save-excursion
                                    insert-buffer-substring.
4.5 Обзор  
4.6 Упражнения  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.1 Поиск дополнительной информации

В этом разделе я опишу каждую новую функцию иногда в деталях, иногда кратко. Если вы заинтересовались, то вы можете получить полную документацию по любой функции Emacs Лисп, в любое время нажав C-h f и затем набрав в мини-буфер имя интересующей вас функции. Аналогичным образом вы можете получить полную документацию по любой переменной, нажав C-h v, и затем имя переменной (потом RET).

Так же, если вы захотите посмотреть исходный код этой функции, то вы можете использовать функцию find-tags, чтобы переместиться туда. Нажмите M-. (то есть нажмите клавишу META и клавишу точка одновременно, или по другому нажмите и отпустите клавишу ESC, и потом нажмите точку. В появившемся мини-буфере наберите имя функции, чей исходный код вы хотите увидеть, например, mark-whole-buffer, после чего нажмите RET. Emacs переключится в другой буфер и отобразит исходный код этой функции на экране вашего компьютера. Чтобы вернуться обратно нажмите C-x b RET.

В зависимости от того как обстоят дела с вашим дистрибутивом Emacs, вам может понадобится описать `таблицу тегов' (tag table), которая обычно хранится в файле `TAGS'. Таблица, которую вы наверняка захотите описать, находится в каталоге `emacs/src'; то есть вы должны использовать команду M-x visit-tags-table, и задать в мини-буфере полный путь к этому файлу например `/usr/local/lib/emacs/19.23/src/TAGS'. See section `Tag Tables' in The GNU Emacs Manual. Также смотрите Create Your Own `TAGS' File, для получения дополнительной информации о том как самому создать таблицу тегов.

После того как вы поближе ознакомитесь с Emacs Лиспом, вы часто будете использовать find-tags для перемещения по исходным кодам Emacs; и часто будете создавать свои собственные таблицы `TAGS'.

Кстати, файлы, содержащие программы на Лиспе обычно называют библиотеками. Такая метафора произошла от использования специализированных библиотек, таких как юридическая библиотека или техническая, а не общественных библиотек. Каждая библиотека, или файл, содержит функции которые связаны с определенной темой или расширением, например, `abbrev.el' управляет сокращениями, `help.el' содержит реализацию встроенной помощи. (Иногда несколько библиотек обеспечивают код для чего то одного, как различные файлы `rmail..' реализуют интерфейс для чтения электронной почты.) В `The GNU Emacs Manual', вы можете встретить упоминание, что команда C-h p позволяет вам производить поиск в стандартных библиотеках Emacs Lisp по ключевым словам.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 Упрощенное определение beginning-of-buffer

Команда beggining-of-buffer очень подходящая функция для старта --- во- первых, вы наверняка уже пользовались ей, и она очень понятна. Если ее использовать как интерактивную команду, beginning-of-buffer перемещает курсор в начало буфера, оставляя метку на предыдущей позиции курсора. Обычно связана с клавишами M-<.

В этой главе мы обсудим укороченную версию этой функции, которая подходит для обычного применения этой функции. Укороченная функция работает как и ожидается, но она не содержит кода для обработки сложных опций. В следующей главе мы опишем полную версию функции. (See section Complete Definition of beginning-of-buffer.)

До рассмотрения исходного кода давайте вспомним, что должно содержаться в определении функции --- оно должно содержать выражение, которое делает функцию интерактивной, так что ее можно вызвать, набрав M-x beginning-of-buffer или нажав клавишу C-<; оно должно содержать код, который оставляет метку в первоначальной позиции курсора в буфере; и код, который перемещает курсор в начало буфера.

А вот и наша укороченная версия этой функции:

 
(defun simplified-beginning-of-buffer ()
  "Переместить курсор в начало буфера,
поставив метку на предыдущей позиции курсора."
  (interactive)
  (push-mark)
  (goto-char (point-min)))

Как и все определения функций, это определение соответствует нашему шаблону для особой форму defun и имеет пять основных частей:

  1. Имя --- в нашем случае simplified-beginning-of-buffer.

  2. Список аргументов --- в нашем случае пустой список ().

  3. Строку документации.

  4. Выражение, которое делает функцию интерактивной.

  5. Тело функции.

В этом определении функции, список аргументов пуст --- это означает что, наша упрощенная версия не требует аргументов. (Когда мы рассмотрим полное определение функции, мы увидим, что ей можно передать необязательный аргумент).

Выражение interactive говорит интерпретатору, что эту функцию можно вызывать интерактивно. В нашем случае у interactive нет аргументов, так как simplified-beginning-of-buffer их и не требует.

Тело состоит из двух строк кода:

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

В первой строке выражение (push-mark). Когда интерпретатор Лиспа вычисляет это выражение, он устанавливает метку на месте текущего положения курсора. Позиция этой метки запоминается в кольце меток (mark ring).

В следующей строке выражение (goto-char (point-min)). Она заставляет курсор прыгнуть к минимально возможной точке в текущем буфере, то есть обычно к его началу (или началу доступной области буфера если он сужен. See section Narrowing and Widening.)

Команда push-mark устанавливает метку на месте предыдущего положения курсора, где он был расположен до того, как выражение (goto-char (point-min)) переместило его в конец буфера. Следовательно, вы можете вернуться на старое место с помощью команды exchange-point-and-mark которая обычно связана с C-x C-x.

Вот и все определение функции!

Когда вы читаете какой-нибудь код и встречаете незнакомую функцию --- например, goto-char, то вы можете выяснить, что же она делает с помощью команды describe-function. Чтобы использовать эту команду, нажмите C-h f и затем в эхо-области наберите имя функции. Команда describe-function напечатает документацию по этой функции в окне `*Help*'. Вот например, документация для функции goto-char:

 
One arg, a number.  Set point to that number.  Beginning of buffer is
position (point-min), end is (point-max).  (Один арг, число.
Устанавливает значение точки равное этому числу.  Начало буфера позиция
(point-min), конец буфера (point-max).)

(Подсказка в эхо-области при использовании describe-function предложит вам символ перед курсором, так что вы можете с удобством использовать эту функцию, расположив курсор после интересующей вас функции и нажав C-h f RET).

Определение функции end-of-buffer аналогично определению beginning-of-buffer только там, где в теле второй функции стоит выражение (goto-char (point-min)); в первой будет (goto-char (point-max)).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3 Определение mark-whole-buffer

Функцию mark-whole-buffer понять не труднее чем simplified-beginning-of-buffer. Однако, в данном случае, мы рассмотрим полную версию функции, а не укороченную.

Функция mark-whole-buffer используется не так часто как функция beginning-of-buffer, но тем не менее очень полезна --- она выделяет весь буфер как один регион, располагая точку в начале, а метку в конце буфера. Она обычно привязана к сочетанию клавиш C-x h.

Полный код для этой функции выглядит следующим образом:

 
(defun mark-whole-buffer ()
 "Поставить точку в начале а метку в конце буфера."
  (interactive)
  (push-mark (point))
  (push-mark (point-max))
  (goto-char (point-min)))

Как и все другие функции, mark-whole-buffer полностью соответствует шаблону для определения функции. Напомним его снова:

 
(defun имя-функции (список-аргументов)
  "докуменация..."
  (выражение-interactive...)
  тело...)

Вот как устроена эта функция: имя функции mark-whole-buffer; за ним следует пустой список аргументов `()', который означает, что функция не требует аргументов. Затем идет строка документации.

Следующая строка содержит выражение (interactive) которое для Emacs означает, что функцию можно будет использовать интерактивно. Это похоже на аналогичные строки функции simplified-beginning-of-buffer, которую мы рассмотрели в предыдущем разделе.

4.3.1 Тело mark-whole-buffer  Только три строчки.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3.1 Тело mark-whole-buffer

Тело функции mark-whole-buffer состоит из трех строчек кода:

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

В первой из этих строк выражение (push-mark (point)).

Это выражение делает ту же самую работу, что и первая строка в теле функции simplified-beginning-of-buffer, в которой написано следующее (push-mark). В обоих случаях интерпретатор Лиспа устанавливает метку в текущее положение курсора.

Я не знаю, почему в mark-whole-buffer написано выражение (push-mark (point)), а в beginning-of-buffer просто (push-mark). Возможно, что тот кто писал код, не знал, что аргумент для функции (push-mark) необязательный и что, если в функцию не передать аргумент, то она автоматически установит метку на месте текущего положения курсора (это действие по умолчанию). Или, возможно, выражение было написано, чтобы соответствовать по длине следующей строке. В любом случае эта строка заставляет Emacs установить метку в текущем положении курсора.

Следующая строка mark-whole-bufferй --- это (push-mark (point-max)). Это выражение устанавливает метку в точке буфера, которая имеет максимальное значение. Это обычно конец буфера (или, если буфер сужен, конец доступной порции буфера. See section Narrowing and Widening, за дополнительной информации об сужении). После того как эта метка была установлена, предыдущая метка, которую мы установили прежде, больше не установлена, но Emacs запомнил ее позицию, как и всех прежде установленных меток. Это значит что вы можете, если желаете вернуться к этой позиции, нажав C-u C-SPC дважды.

Последняя строка этой функции --- выражение (goto-char (point-min)). Она выглядит точно также как и в функции beginning-of-buffer. Это выражение перемещает курсор к точке буфера которая имеет минимальное значение, обычно к началу буфера (или к началу доступной области буфера, в случае сужения). В результате работы всей функции, точка помещается в начале буфера, метка в конце. То есть весь буфер следовательно регион.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4 Определение append-to-buffer

Команда append-to-buffer почти так же проста, как и команда mark-whole-buffer. Она копирует регион (то есть часть буфера между точкой и меткой) из текущего буфера в заданный.

В теле команды append-to-buffer для копирования региона используется функция insert-buffer-substring. Принцип работы функции insert-buffer-substring (вставить-в-буфер-подстроку) заложен в ее названии --- она берет символьную строку из части буфера --- "подстроку" и вставляет ее в другой буфер. Большая часть действий в функции append-to-buffer --- это подготовка параметров для работы функции insert-buffer-substring; мы должны будем задать буфер куда нам необходимо скопировать текст, а также регион, который мы будем копировать. А вот и полный текст функции:

 
(defun append-to-buffer (buffer start end)
  "Добавить к заданному буферу текст в регионе.
Текст вставляется в буфер перед точкой.

При вызове принимает три аргумента:
буфер или его имя, и два числа задающие регион в
текущем буфере."
  (interactive "BAppend to buffer: \nr")
  (let ((oldbuf (current-buffer)))
    (save-excursion
      (set-buffer (get-buffer-create buffer))
      (insert-buffer-substring oldbuf start end))))

Работу этой функции мы сможем лучше понять, если посмотрим на нее как на серию уже известных нам шаблонов.

Самый верхний шаблон --- само определение функции. В этом случае он выглядит следующим образом (мы заполнили в нем несколько слотов):

 
(defun append-to-buffer (buffer start end)
  "докуменация..."
  (interactive "BAppend to buffer: \nr")
  тело...)

Первая строка функции включает ее имя и три аргумента. Эти аргументы: buffer (куда мы и будем копировать текст), start и end (соответственно начало и конец региона в текущем буфере откуда мы и будем копировать текст.)

Следующая часть функции --- это документация, которая как всегда, должна быть понятной и простой

4.4.1 Интерактивное выражение в append-to-buffer  Две части выражения interactive.
4.4.2 Тело append-to-buffer  
4.4.3 save-excursion в append-to-buffer  Как работает the save-excursion.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4.1 Интерактивное выражение в append-to-buffer

Поскольку функцию append-to-buffer предполагается использовать интерактивно, то в ней есть выражение interactive. (Чтобы повторить interactive, смотри Making a Function Interactive). Это выражение выглядит следующим образом:

 
(interactive "BAppend to buffer: \nr")

Аргумент к interactive --- это строка, состоящая из двух частей, разделенных символом `\n'.

Первая часть --- это `BAppend to buffer: ' (добавить к буферу). Здесь `B' --- один из предопределенных символов для interactive, который означает имя буфера, которое будет передано в функцию. Emacs запросит имя буфера, отобразив подсказку в мини-буфере, для формирования которой используется строка, которая следует за `B' (в нашем случае это `Append to buffer: '). После ввода запрашиваемого буфера Emacs свяжет переменную buffer в списке аргументов с заданным буфером.

Символ новой строки, `\n', отделяет первую часть аргумента от второй части. Затем следует `r', который говорит Emacs связать два аргумента, которые следуют за символом buffer в списке аргументов функции (то есть start и end) к значениям точки и метки.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4.2 Тело append-to-buffer

Тело функции append-to-buffer начинается с let.

Как мы уже видели ранее, (see section let) цель выражения let --- создать и присвоить первоначальные значения к одной и более переменных, которые будут использоваться только в теле let. Это значит, что такую переменную нельзя будет спутать с любой переменной, носящей такое же имя вне выражения let.

Мы можем видеть как выражение let входит в состав функции, показав шаблон для append-to-buffer с выделенным выражением let:

 
(defun append-to-buffer (buffer start end)
  "документация..."
  (interactive "BAppend to buffer: \nr")
  (let ((переменная значение))
        тело...)

В составе выражения let три элемента:

  1. Сам символ let;

  2. Список переменных: в нашем случае один двух-элементный список, (переменная значение);

  3. Тело выражения let.

В функции append-to-buffer список переменных выглядит следующим образом:

 
(oldbuf (current-buffer))

Здесь одна переменная oldbuf связывается со значением, возвращаемым после вычисления выражения (current-buffer). Переменная oldbuf, используется для запоминания буфера, в котором вы работаете в данный момент.

Элемент или элементы в списке переменных окружаются скобками так, чтобы интерпретатор Лиспа мог отделить список переменных от тела выражения let. В результате двухэлементный список внутри списка переменных окружен дополнительными скобками. Все вместе выглядит следующим образом:

 
(let ((oldbuf (current-buffer)))
  ... )

Две скобки перед oldbuf могут вызвать удивление, если вы не вспомните, что первая скобка это граница всего списка переменных, а вторая отмечает начало двухэлементного списка (oldbuf (current-buffer)).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4.3 save-excursion в append-to-buffer

Тело выражения let в append-to-buffer состоит из выражения save-excursion.

Функция save-excursion запоминает местоположение точки и метки, и затем восстанавливает их этим значениям после завершения выполнения тела save-excursion. В добавок save-excursion запоминает первоначальный буфер и также его восстанавливает. Именно поэтому save-excursion используется в append-to-buffer.

Кстати, вы заметили, что функции в Лиспе обычно форматируются так, что все входящее в состав многострочного списка выравнивается немного правее, чем первый символ этого списка. В этой функции, например, let выравнено немного правее чем defun, а save-excursion правее чем let, то есть следующим образом:

 
(defun ...
  ...
  ...
  (let...
    (save-excursion
      ...

Такое соглашение о форматировании позволяет легко определить, что две строки в теле save-excursion закрытые в скобки, связаны с save-excursion, а само выражение save-excursion закрыто в скобки, связанные с let:

 
(let ((oldbuf (current-buffer)))
  (save-excursion
    (set-buffer (get-buffer-create buffer))
    (insert-buffer-substring oldbuf start end))))

Использование функции save-excursion полностью соответствует следующему шаблону:

 
(save-excursion
  первое-выражение-в-теле
  второе-выражение-в-теле
   ...
  последнее-выражение-в-теле)

В нашей функции тело save-excursion содержит только два выражения. Тело выглядит следующим образом:

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

Когда вычисляется функция append-to-buffer, эти два выражения вычисляются последовательно. Значение последнего выражения возвращается как значение самой функции save-excursion; другое выражение вычисляется только за его побочный эффект.

В первой строке тела save-excursion используется функция set-buffer, для того, чтобы изменить текущий буфер на тот, который передан функции append-to-buffer как первый аргумент. (Изменение буфера --- это побочный эффект; как мы говорили ранее в Лиспе побочный эффект это часто основная вещь, которой мы добиваемся). Во второй строке и выполняется вся основная работа функции.

Функция set-buffer переключает внимание Emacs к буферу, куда мы будем копировать текст, из которого нас вернет save-excursion. Эта строка выглядит следующим образом:

 
(set-buffer (get-buffer-create buffer))

Вложенное в этот список выражение --- это (get-buffer-create buffer). Это выражение использует функцию get-buffer-create, которая или получает имя существующего буфера или, если его не существует, то создает такой буфер с данным именем. Это значит, что вы можете использовать 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 копирует строку из буфера, заданного первым аргументом и вставляет строку в текущий буфер. В нашем случае аргумент к insert-buffer-substring --- это значение переменной созданной выражением let, то есть значение oldbuf, который был текущим буфером на момент выполнения команды append-to-buffer.

После того, как insert-buffer-substring закончит работу, save-excursion восстановит первоначальный буфер и append-to-buffer сделала свою работу.

В схематичной форме, работу тела можно представить так:

 
(let (связать-oldbuf-со-значением-current-buffer)
  (save-excursion                       ; Запомнить буфер, точку, метку.
    изменить-буфер
    вставить-текст-из-oldbuf-в-буфер)

  вернуться-назад-к-первоначальному-буферу
пусть-локальное-значение-oldbuf-изчезнет

Если подвести итог, то append-to-buffer работает следующим образом --- она сохраняет значение текущего буфера в переменной oldbuf. Затем или получает новый буфер, или создает его, если это необходимо, и переключает внимание Emacs на него. Используя значение oldbuf вставляет регион текста из старого буфера в новый буфер; и после этого используя save-excursion, возвращает вас в обратно в первоначальный буфер.

Рассмотрев append-to-buffer, мы изучили довольно сложную функцию. Она демонстрирует использование let и save-excursion, способы изменения текущего буфера и как возвращения обратно из другого буфера. Многие другие функции точно также используют let, save-excursion, set-buffer.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.5 Обзор

Ниже краткие описания функций которые мы обсудили в этой главе.

describe-function
describe-variable
Отображает документацию для функции или переменной. Обычно связана с клавишами C-h f и C-h v.

find-tag
Находит файл содержащий исходный текст функции или объявление переменной и переключает буфер туда, поместив курсор в начало искомой функции или переменной. Обычно связана с M-.(клавиша META за которой следует точка).

save-excursion
Сохраняет значение точки и метки и восстанавливает их после выполнения тела save-excursion. Также запоминает текущий буфер и восстанавливает его тоже.

push-mark
Устанавливает метку и записывает значение предыдущей метки в кольцо меток. Метка --- это положение в буфере, которое запоминает свою относительную позицию даже если какой-нибудь текст добавить или удалить из буфера.

goto-char
Устанавливает точку в место описанное значением аргумента, которое будет числом, маркером, или выражением, которое возвращает позицию --- например, (point-min).

insert-buffer-substring
Копирует регион текста из буфера, который передается как аргумент к функции, и вставляет регион в текущий буфер.

mark-whole-buffer
Пометить весь буфер как регион. Обычно связано с C-x h.

set-buffer
Переключить внимание Emacs на другой буфер, но не изменять содержание текущего окна. Используется когда над другим буфером будет работать программа, а не человек.

get-buffer-create
get-buffer
Найти буфер с заданным именем или создать новый, если буфера с таким именем не существует. Функция get-buffer возвращает nil, если буфера с заданным именем не существует.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.6 Упражнения


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated on March, 10 2004 using texi2html