Выбрать главу

            (setq result (concat (substring result 0 (match-beginning 0))

                                 "."

                                 (substring result (match-end 0)))))

          (concat "package " result ";"))

      "")))

(defun template-insert-class-name ( )

  "Inserts the name of the java class being defined in the current file,

based on the file name. If not a Java source file, inserts nothing."

  (let ((name (file-name-nondirectory (buffer-file-name))))

    (if (string-match "\\(.*\\)\\.java" name)

        (substring name (match-beginning 1) (match-end 1))

      "")))

(provide 'template)

You'll notice that this code makes heavy use of the regular expression facilities, which is no surprise. The first section sets up some variables that configure the operation of the template system. template-file-name determines the file name (or prefix) that is used to search for templates; the default value of file-template is probably fine. template-replacements-alist sets up the standard placeholders, and the mechanism by which they get replaced by appropriate values. Adding entries to this list is one way to extend the system. Each entry consists of the placeholder to be replaced, followed by the Lisp function to be executed to produce its replacement. The way this function can be stored in a list and executed when appropriate later is one of the great things about Lisp and is discussed in more depth in the calculator mode example in the next section. The placeholders supported are:

%filename%

Gets replaced by the name of the file being created.

%creator%, %author%

These are synonyms; both get replaced by the name of the user creating the file.

%date%

Turns into the current date and time when the file is created.

%once%

Expands into boilerplate code for the C preprocessor to cause a header file to include itself only once, even if it's been included multiple times by other header files. (This sort of thing has been taken care of in more modern environments like Objective C and Java but can still be handy when working with traditional C compilers.)

%package%

Is replaced by the Java package which contains the file being created (assuming the file is a Java class). This package is determined by examining the directory structure in which the file is being placed.

%class%

Becomes the name of the Java class being defined in the file, assuming it's a Java source file.

The first function, find-template-file, is responsible for searching the directory hierarchy above the file being created, looking for a file with the right name to be considered a file template (if template-file-name has been left at its default value, this looks for either a file named file-template or file-template-ext where ext is the extension at the end of the name of the file being created). It just keeps lopping the last directory off the path in which it's looking, starting with the location of the new file, and seeing if it can read a file with one of those names in the current directory, until it runs out of directories.

The function template-file-not-found-hook is the "main program" of the template system. It gets "hooked in" to the normal Emacs find-file process, and called whenever find-file doesn't find the file the user asked for (in other words, a new file is being created). It uses condition-case (a mechanism similar to exception handling in C++ and Java) to make sure it gets a chance to clean up after itself if the user cancels the process of filling in the template file. It checks whether the template file can be found, asks users if they want to use it, and (if they do) loads it into the new buffer and performs the placeholder substitutions. For an explanation of the list manipulation and funcall code that makes the substitutions work, read the discussion of Calculator mode in the next section. Finally, it jumps to the beginning of the new buffer and marks it as unchanged (because, as far as users are concerned, it's a brand new buffer on which they've not yet had to expend any effort).

Immediately after the function definition is the chunk of code that hooks it into the find-file mechanism. The file-not-found-hooks is a variable that Emacs uses to keep track of things to do when a requested file is not found. (Giving you opportunities to change or enhance normal behavior through "hooks" is a wonderful trait of Emacs that is discussed in more depth following the Calculator mode example later in this chapter.) Our code checks to make sure it's not already hooked up (so you don't end up having it run twice or more if you re-load the library file during an Emacs session), and then installs our hook at the end of the list if it's not there.

The rest of the file is helper functions to handle the more complex placeholders. template-insert-java-package figures out the value that should replace %package%, while template-insert-class-name figures out the Java class name that replaces %class%.

The last function call in the file, (provide 'template), records the fact that a "feature" named "template" has been loaded successfully. The provide function works with require to allow libraries to be loaded just once. When the function (require 'template) is executed, Emacs checks whether the feature "template" has ever been provided. If it has, it does nothing, otherwise, it calls load-library to load it. It's a good practice to have your libraries support this mechanism, so that they can be gracefully and efficiently used by other libraries through the require mechanism. You'll find this pattern throughout the Emacs library sources.

11.5 Programming a Major Mode

After you get comfortable with Emacs Lisp programming, you may find that that "little extra something" you want Emacs to do takes the form of a major mode. In previous chapters, we covered major modes for text entry, word processor input, and programming languages. Many of these modes are quite complicated to program, so we'll provide a simple example of a major mode, from which you can learn the concepts needed to program your own. Then, in the following section, you will learn how you can customize existing major modes without changing any of the Lisp code that implements them.

We'll develop Calculator mode, a major mode for a calculator whose functionality will be familiar to you if you have used the Unix dc (desk calculator) command. It is a Reverse Polish (stack-based) calculator of the type made popular by Hewlett-Packard. After explaining some of the principal components of major modes and some interesting features of the calculator mode, we will give the mode's complete Lisp code.

11.5.1 Components of a Major Mode

A major mode has various components that integrate it into Emacs. Some are:

• The symbol that is the name of the function that implements the mode