[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
В этой главе мы детально изучим несколько реальных функций, написанных на 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] | [ ? ] |
В этом разделе я опишу каждую новую функцию иногда в деталях, иногда кратко. Если вы заинтересовались, то вы можете получить полную документацию по любой функции 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] | [ ? ] |
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
и имеет пять основных частей:
simplified-beginning-of-buffer
.
()
.
В этом определении функции, список аргументов пуст --- это означает что, наша упрощенная версия не требует аргументов. (Когда мы рассмотрим полное определение функции, мы увидим, что ей можно передать необязательный аргумент).
Выражение 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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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] | [ ? ] |
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
три элемента:
let
;
(переменная значение)
;
let
.
В функции append-to-buffer
список переменных выглядит следующим
образом:
(oldbuf (current-buffer)) |
Здесь одна переменная oldbuf
связывается со значением,
возвращаемым после вычисления выражения (current-buffer)
.
Переменная oldbuf
, используется для запоминания буфера, в
котором вы работаете в данный момент.
Элемент или элементы в списке переменных окружаются скобками так,
чтобы интерпретатор Лиспа мог отделить список переменных от тела
выражения let
. В результате двухэлементный список внутри списка
переменных окружен дополнительными скобками. Все вместе выглядит
следующим образом:
(let ((oldbuf (current-buffer))) ... ) |
Две скобки перед oldbuf
могут вызвать удивление, если вы не
вспомните, что первая скобка это граница всего списка переменных, а
вторая отмечает начало двухэлементного списка (oldbuf
(current-buffer))
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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 (связать- |
Если подвести итог, то append-to-buffer
работает следующим
образом --- она сохраняет значение текущего буфера в переменной
oldbuf
. Затем или получает новый буфер, или создает его, если
это необходимо, и переключает внимание Emacs на него. Используя
значение oldbuf
вставляет регион текста из старого буфера в
новый буфер; и после этого используя save-excursion
, возвращает
вас в обратно в первоначальный буфер.
Рассмотрев append-to-buffer
, мы изучили довольно сложную
функцию. Она демонстрирует использование let
и
save-excursion
, способы изменения текущего буфера и как
возвращения обратно из другого буфера. Многие другие функции точно
также используют let
, save-excursion
, set-buffer
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Ниже краткие описания функций которые мы обсудили в этой главе.
describe-function
describe-variable
C-h v
.
find-tag
save-excursion
save-excursion
. Также запоминает текущий буфер и
восстанавливает его тоже.
push-mark
goto-char
(point-min)
.
insert-buffer-substring
mark-whole-buffer
set-buffer
get-buffer-create
get-buffer
get-buffer
возвращает nil
,
если буфера с заданным именем не существует.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
simplified-end-of-buffer
затем проверьте
ее, чтобы увидеть как она работает.
if
и get-buffer
,
которая печатает сообщение говорящее существует ли буфер имя которого
вы задаете как аргумент к этой функции
find-tag
, найдите исходный код для функции
copy-to-buffer
.
Using find-tag
, find the source for the copy-to-buffer
function.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |