If you try using these functions, though, you'll find they have some noticeable drawbacks compared to the other tag insertion commands in HTML mode. For one thing, while the other helper commands leave your cursor in between the opening and closing tags, our insertions leave the cursor after the closing tag, which is not only inconsistent, but it's much less helpful. Also, while the other tags you insert can be customized in terms of your preferred capitalization, or wrapped around existing content in the document, our simple-minded insertcalls give us no such capabilities.
Luckily, it's not hard to add the smarts we want. It turns out that HTML mode is defined in the file sgml-mode.el (we learned this by applying help's handy describe-functioncommand, C-h f, to the mode-defining function HTML mode. Armed with this knowledge, it was an easy matter to pull up and study the Lisp code that makes it work using the find-library-fileutility shown in "A Treasure Trove of Examples" earlier in this chapter. A little quick hunting to find a parallel example revealed that the tag support is implemented using a skeletal function generator. Without going into too much detail, it turns out that the code we want to use is this:
(define-skeleton html-head
"HTML document header section."
nil
"
" _ "")
(define-skeleton html-title
"HTML document title."
nil
"")
The define-skeleton function sets up the skeletal HTML code to be inserted, and it does this by writing a Lisp function based on the template you pass it. Its first argument is the name of the Lisp function to define, and the next is a documentation string for that function explaining what it inserts. After that comes an optional prompt that can be used to customize the content to be inserted. We don't need any customization, so we leave it as nil
to skip the prompt. Finally comes the list of strings to be inserted, and we mark where we want the cursor to end up with " _
". (To learn more about the way this skeleton system works, invoke describe-functionon insert-skeleton.)
With these changes, our new commands work just like the other insertion tools in HTML mode. Even more than the specific Lisp code that came out of this example, the technique we used to create it is worth learning. If you can develop the skills and habits involved in tracking down an example from the built-in libraries that is close to what you want, and digging into how it works just enough to come up with a variant that solves your problem, you'll be well on your way to becoming the friendly Emacs Lisp guru your friends rely on when they need a cool new trick.
Here is a third example. Let's say you program in C, and you want a Lisp function that counts the number of C function definitions in a file. The following function does the trick; it is somewhat similar to the count-lines-bufferexample earlier in the chapter. The function goes through the current buffer looking for (and counting) C function definitions by searching for {
at the beginning of a line (admittedly, this simplistic approach assumes a particular and rigid C coding style):
(defun count-functions-buffer ( )
"Count the number of C function definitions in the buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(let ((count 0))
(while (re-search-forward "^{" nil t)
(setq count (1+ count)))
(message "%d functions defined." count))))
The re-search-forwardcall in this function has two extra arguments; the third (last) of these means "if not found, just return nil
, don't signal an error." The second argument must be set to nil
, its default, so that the third argument can be supplied. [83]
Now assume we want to bind this function to C-c fin C mode. Here is how we would set the value of c-mode-hook:
(add-hook 'c-mode-hook
'(lambda ( )
(define-key c-mode-map "\C-cf" 'count-functions-buffer)))
Put this code and the function definition given earlier in your .emacs file, and this functionality will be available to you in C mode.
As a final example of mode hooks, we'll make good on a promise from the previous chapter. When discussing C++ mode, we noted that the commands c-forward-into-nomenclatureand c-backward-into-nomenclatureare included as alternatives to forward-wordand backward-wordthat treat WordsLikeThis as three words instead of one, and that this feature is useful for C++ programmers. The question is how to make the keystrokes that normally invoke forward-wordand backward-wordinvoke the new commands instead.
At first, you might think the answer is simply to create a hook for C++ mode that rebinds M-fand M-b, the default bindings for forward-wordand backward-word, to the new commands, like this:
(add-hook
'c++-mode-hook
'(lambda ( )
(define-key c++-mode-map "\ef"
'c-forward-into-nomenclature)
(define-key c++-mode-map "\eb"
'c-backward-into-nomenclature)))
(Notice that we are using c++-mode-map, the local keymap for C++ mode, for our key bindings.) But what if those keys have already been rebound, or what if forward-wordand backward-wordare also bound to other keystroke sequences (which they usually are anyway)? We need a way to find out what keystrokes are bound to these functions, so that we can reset all of them to the new functions.
Luckily, an obscure function gives us this information, where-is-internal. This function implements the "guts" of the where-ishelp command, which we will see in Chapter 14 Chapter 14. The Help System Emacs has the most comprehensive help facility of any text editor—and one of the best such facilities of any program at all. In fact, the Emacs help facilities probably cut down the time it took for us to write this book by an order of magnitude, and they can help you immeasurably in your ongoing quest to learn more about Emacs. In this chapter, we describe Emacs help in the following areas: • The tutorial. • The help key ( C-h ) and Help menu, which allow you to get help on a wide variety of topics. • The help facilities of complex commands like query-replace and dired . • Navigating Emacs manuals and using the info documentation reader. • Completion , in which Emacs helps you finish typing names of functions, variables, filenames, and more. Completion not only saves you time and helps you complete names of functions you know about but can help you discover new commands and variables.
. where-is-internalreturns a list of keystroke atoms that are bound to the function given as an argument. We can use this list in a whileloop to do all of the rebinding necessary. Here is the code:
(add-hook 'c++-mode-hook
'(lambda ( )
(let ((fbinds (where-is-internal 'forward-word))
(bbinds (where-is-internal 'backward-word)))
(while fbinds
(define-key c++-mode-map (car fbinds)
'c-forward-into-nomenclature)
Читать дальше