(setq fbinds
(cdr fbinds)))
(while bbinds
(define-key c++-mode-map (car bbinds)
'c-backward-into-nomenclature)
(setq bbinds (cdr bbinds))))))
The two lines in the top of the letstatement get all of the key bindings of the commands forward-wordand backward-wordinto the local variables fbindsand bbinds, respectively.
After that, there are two whileloops that work like the print-stackfunction of the calculator mode shown earlier in this chapter. This use of whileis a very common Lisp programming construct: it iterates through the elements of a list by taking the first element (the car), using it in some way, and deleting it from the list ( (setq list (cdr list )
). The loop finishes when the list becomes empty ( nil
), causing the whiletest to fail.
In this case, the first whileloop takes each of the bindings that where-is-internalfound for forward-wordand creates a binding in C++ mode's local keymap, c++-mode-map, for the new command c-forward-into-nomenclature. The second whileloop does the same for backward-wordand c-backward-into-nomenclature.
The surrounding code installs these loops as a hook to C++ mode, so that the rebinding takes place only when C++ mode is invoked and is active only in buffers that are in that mode.
One final word about hooks: you may have noticed that some of the mode customizations we have shown in previous chapters include hooks and others do not. For example, the code in the previous chapter to set your preferred C or C++ indentation style included a hook:
(add-hook 'c-mode-hook
'(lambda ( )
(c-set-style " stylename ")
(c-toggle-auto-state)))
whereas the code that sets an alternative C preprocessor command name for the c-macro-expandcommand did not:
(setq c-macro-preprocessor "/usr/local/lib/cpp -C")
Why is this? Actually, the correct way to customize any mode is through its hook—for example, the preceding example should really be:
(add-hook 'c-mode-hook
'(lambda ( )
(setq c-macro-preprocessor "/usr/local/lib/cpp -C")))
If you merely want to set values of variables, you can get away without a hook, but a hook is strictly required if you want to run functions like c-set-styleor those used to bind keystrokes. The precise reason for this dichotomy takes us into the murky depths of Lisp language design, but it's essentially as follows.
Variables that are local to modes, like c-macro-preprocessor, do not exist if you don't invoke the mode in which they are defined. So, if you aren't editing C or C++ code, then c-macro-preprocessordoesn't exist in your running Emacs, because you haven't loaded C mode (see below). Yet if your .emacs file contains a setqto set this variable's value, then you call the variable into existence whether or not you ever use C mode. Emacs can deal with this: when it loads C mode, it notices that you have already set the variable's value and does not override it.
However, the situation is different for functions. If you put a call to a mode-local function like c-set-stylein your .emacs file, then (in most cases) Emacs complains, with the message Error in init file
, because it does not know about this function and thus cannot assume anything about what it does. Therefore you must attach this function to a hook for C mode: by the time Emacs runs your hook, it has already loaded the mode and therefore knows what the function does.
These examples of hooks are only the briefest indication of how far you can go in customizing Emacs's major modes. The best part is that, with hooks, you can do an incredible amount of customization without touching the code that implements the modes. In exchange, you should remember, when you do write your own modes, to think about useful places to put hooks so others can take advantage of them.
11.7 Building Your Own Lisp Library
After you have become proficient at Emacs Lisp programming, you will want a library of Lisp functions and packages that you can call up from Emacs at will. Of course, you can define a few small functions in your .emacs file, but if you are writing bigger pieces of code for more specialized purposes, you will not want to clutter up your .emacs file—nor will you want Emacs to spend all that time evaluating the code each time you start it up. The answer is to build your own Lisp library, analogous to the Lisp directories that come with Emacs and contain all of its built-in Lisp code. After you have created a library, you can load whatever Lisp packages you need at a given time and not bother with the others.
Creating a library requires two simple steps. First, create a directory in which your Lisp code will reside. Most people create a elisp subdirectory of their home directory. Lisp files are expected to have names ending in .el (your .emacs file is an exception). The second step is to make your directory known to Emacs so that when you try to load a Lisp package, Emacs knows where to find it. Emacs keeps track of such directories in the global variable load-path, which is a list of strings that are directory names.
The initial value for load-pathis populated with the names of the Lisp directories that come with Emacs, e.g., /usr/local/emacs/lisp . You will need to add the name of your own Lisp directory to load-path. One way to make this addition is to use the Lisp function append, which concatenates any number of list arguments together. For example, if your Lisp directory is ~/lisp , you would put the following in your .emacs file:
(setq load-path (append load-path (list "~ yourname /lisp")))
The function listis necessary because all of the arguments to appendmust be lists. This line of code must precede any commands in your .emacs file that load packages from your Lisp directory.
When you load a library, Emacs searches directories in the order in which they appear in load-path; therefore, in this case, Emacs searches its default Lisp directory first. If you want your directory to be searched first, you should use the consfunction described earlier instead of append, as follows:
(setq load-path (cons "~ yourname /lisp" load-path))
This form is useful if you want to replace one of the standard Emacs packages with one of your own. For example, you'd use this form if you've written your own version of C mode and want to use it instead of the standard package. Notice that the directory name here is not surrounded by a call to listbecause cons's first argument can be an atom (a string in this case). This situation is similar to the use of consfor pushing values onto stacks, as in the calculator mode described earlier.
If you want Emacs to search the directory you happen to be in at any given time, simply add nil
to load-path, either by prepending it via consor by appending it via append. Taking this step is analogous to putting .
in your Unix PATH environment variable.
Читать дальше