liberte@B.CS.UIUC.EDU (Daniel LaLiberte) (12/20/86)
This is part 1 of 2 of the documentation prepared so far. Hope it helps. Please mail comments to the indicated authors. If you wish to join the mailing list of volunteers, mail to: gnu-manual-request@a.cs.uiuc.edu. Dan LaLiberte liberte@b.cs.uiuc.edu liberte@uiuc.csnet ihnp4!uiucdcs!liberte ------------------------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh. # The following files will be created: # outline # tutorial # This archive created: Sat Dec 20 00:43:53 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'outline'" '(9622 characters)' if test -f 'outline' then echo shar: over-writing existing file "'outline'" fi sed 's/^X//' << \SHAR_EOF > 'outline' X XCopyright (C) 1986 Daniel LaLiberte X XGNU Emacs Programmers Manual (outline) X XGNU Emacs Lisp X XPart I. Lisp. X XObjects and types: X atom, integer, symbol, cons, lists, vector, string, X buffer, marker, built-in-function, window, process X XLists: X atom X cons X list X make-list X eq X equal X null X consp X listp X nlistp X sequencep X car X car-safe X cdr X cdr-safe X setcar X setcdr X append X nthcdr X nth X memq X assq X assoc X rassq X delq X nreverse X reverse X sort X nconc X mapcar X XNumbers: X integerp X natnump X zerop X integer-or-marker-p X = < > <= >= /= X int-to-string X string-to-int X + - * / % X 1+ X 1- X max X min X longand X logior X logxor X lognot X ash X lsh X random X XArrays Vectors Sequences: X make-vector X vector X vectorp X stringp X arrayp X sequencep X aref X aset X copy-sequence X length X elt X fillarray X XStrings: X make-string X stringp X int-to-string X string-to-int X char-to-string X string-to-char X concat X vconcat X substring X string-equal X string-lessp X XSymbols: X General X name, value as variable, function definition, X property list, X get X put X intern X intern-soft X mapatoms X make-symbol X symbolp X boundp X fboundp X makunbound X fmakeunbound X symbol-function X symbol-plist X symbol-name X symbol-value X default-value X fset X set X set-default X setplist X XLisp expressions: X variables vs constants vs X function calls vs special forms (a funny sort of function call) X Mention eval setq quote. X Macros X defmacro X macroexpand X XFunctions: X function X apply X funcall X defun X autoload X fboundp X fmakunbound X Simple args and rest args. X identity X call-interactively X prefix-numeric-value X interactive X interactive-p X commandp X backtrace-debug X backtrace X mapatoms X mapconcat X XVariables and binding: X setq X defvar X defconst X boundp X makunbound X Explain dynamic (vs lexical) scope. X let let* X Variables that Emacs primitives look at vs. X Variables that users may access X user-variable-p X set-variable X Per-buffer variables (there are two kinds of these) X make-variable-buffer-local X make-local-variable X global-value X global-set X hack-local-variables X X XControl Structures X progn X prog1 X prog2 X or X and X cond X if X while X catch X throw X unwind-protect X eval X apply X funcall X mapcar X mapconcat X XErrors X unwind-protect X condition-case X signal X error X backtrace-debug X backtrace X XEvaluation, Compilation, and Debugging X load X provide X require X features X eval-region X eval-current-buffer X eval-expression X edit-and-eval-command X byte-compile-file X byte-recompile-directory X batch-byte-compile X debug X debug-on-entry X cancel-debug-on-entry X Variables: X debug-on-entry X debug-on-error X XLisp I/O to streams. X read X print X prin1 X princ X Strings as streams. X Buffers as streams. X Markers as streams. X Functions as streams. X Minibuffer streams. X X------------------------------------------------------ X X XPart II. Lisp and Editing X X top-level X recursion-depth X recursive-edit X exit-recursive-edit X abort-recursive-edit X save-excursion X X XBuffers X buffer-list X get-buffer X get-file-buffer X get-buffer-create X generate-new-buffer X buffer-name X buffer-number X buffer-file-name X buffer-local-variables X buffer-modified-p X set-buffer-modified-p X rename-buffer X other-buffer X buffer-flush-undo X kill-buffer X switch-to-buffer X pop-to-buffer X current-buffer X set-buffer X barf-if-buffer-read-only X bury-buffer X erase-buffer X list-buffers X lock-buffer X unlock-buffer X X buffer-size X point-min X point-max X XStandard per-buffer variables and their uses X X make-variable-buffer-local X make-local-variable X kill-local-variable X kill-all-local-variables X X XPositions X point X window-point X set-window-point X register-to-point X point-to-register X buffer-size X point-max X point-min X bobp X eobp X bolp X eolp X beginning-of-buffer X end-of-buffer X goto-char X goto-line X forward-char X backward-char X forward-line X beginning-of-line X end-of-line X move-to-column X X following-char X preceding-char X char-after X buffer-substring X buffer-string X X X markers X mark X set-mark X set-mark-command X exchange-point-and-mark X pop-mark X push-mark X process-mark X abbrev-prefix-mark X mark-marker X mark-word X mark-paragraph X mark-sexp X mark-page X mark-whole-buffer X X markerp X integer-or-marker-p X make-marker X set-marker X point-max-marker X point-min-marker X marker-position X marker-buffer X point-marker X insert-before-markers X X region-beginning X region-end X count-lines-region X X clipping restrictions X narrow-to-region X widen X point-max X point-min X point-max-marker X point-min-marker X save-restriction X narrow-to-page X XInserting and Deleting X insert X quoted-insert X insert-before-markers X self-insert-and-exit X self-insert-command X insert-buffer-substring X newline X split-line X open-line X X delete-region X subst-char-in-region X XBuffers and Regions X insert-buffer X append-to-buffer X prepend-to-buffer X copy-to-buffer X X subst-char-in-region X zap-to-char X X delete-char X delete-backward-char X etc. X X undo X buffer-flush-undo X undo-boundary X undo-more X undo-start X buffer-modified X X XSearching and matching X X looking-at X string-match X scan-buffer X skip-chars-forward X skip-chars-backward X search-backward X search-forward X word-search-backward X word-search-forward X re-search-backward X re-search-forward X replace-match X match-beginning X match-end X match-data X store-match-data X regexp-quote X X XParsing and scanning X XSyntax Tables X syntax-table-p X syntax-table X standard-syntax-table X copy-syntax-table X set-syntax-table X char-syntax X describe-syntax X X XMajor and Minor Modes X XKeymaps X keymapp X make-keymap X make-sparse-keymap X define-key X lookup-key X key-binding X local-key-binding X global-key-binding X global-set-key X local-set-key X global-unset-key X local-unset-key X define-prefix-command X use-global-map X use-local-map X current-local-map X accessible-keymaps X key-description X single-key-description X text-char-description X where-is-internal X where-is X describe-bindings X apropos X execute-extended-command X substitute-command-keys X X XSelf-inserting commands X X auto-fill X parenthesis matching X Abbreviations X XMinibuffer: X read-minibuffer X eval-minibuffer X read-string X read-no-blanks-input X read-command X read-function X read-variable X read-buffer X try-completion X all-completions X minibuffer-complete X minibuffer-complete-and-exit X minibuffer-completion-help X self-insert-and-exit X exit-minibuffer X Relate this to interactive. X Minibuffer keymaps. X XInput/Output X X read-key-sequence X command-execute X input-pending-p X recent-keys X this-command-keys X open-dribble-file X discard-input X set-input-mode X X message X format X X write-char X with-output-to-temp-buffer X terpri X prin1 X prin1-to-string X princ X print X read-char X get-file-char X X read X read-from-string X X XFiles X File data transfer. X File manipulation. X X list-directory X directory-files X file-name-completion X file-name-all-completions X file-attributes X X X file-name-directory X file-name-nondirectory X make-temp-name X expand-file-name X substitute-in-file-name X copy-file X delete-file X rename-file X add-name-to-file X make-symbolic-link X file-exists-p X file-writable-p X file-symlink-p X file-directory-p X file-modes X set-file-modes X file-nlinks X X X recover-file X kill-some-buffers X auto-save-mode X make-auto-save-file-name X auto-save-file-name-p X save-buffers-kill-emacs X normal-mode X set-auto-mode X hack-local-variables X set-visited-file-name X write-file X backup-buffer X find-backup-file-name X save-buffer X basic-save-buffer X delete-auto-save-file-if-necessary X save-some-buffers X not-modified X toggle-read-only X do-auto-save X set-buffer-auto-saved X recent-auto-save-p X read-file-name-internal X read-file-name X X X insert-file-contents X insert-file X append-to-file X revert-buffer X recover-file X X write-region X verify-visited-file-modtime X clear-visited-file-modtime X X X find-file X find-file-other-window X find-file-read-only X find-alternate-file X create-file-buffer X find-file-noselect X after-find-file X X X XCommands and Keymaps X interactive X call-interactively X vectors as key definitions. X strings as key definitions (macros). X Manipulating keymaps. X XWindows X windowp X pos-visible-in-window-p X window-buffer X window-height X window-width X window-hscroll X set-window-hscroll X window-edges X window-point X window-start X set-window-point X set-window-start X delete-window X next-window X previous-window X other-window X get-lru-window X get-largest-window X get-buffer-window X delete-other-windows X delete-windows-on X replace-buffer-in-windows X show-buffer X selected-window X select-window X display-buffer X split-window X enlarge-window X shrink-window X X split-window-vertically X split-window-horizontally X enlarge-window-horizontally X shrink-window-horizontally X X X scroll-up X scroll-down X scroll-left X scroll-right X scroll-other-window X recenter X move-to-window-line X save-window-excursion X X redraw-display X sit-for X sleep-for X ding X X open-termscript X set-screen-height X set-screen-width X screen-height X screen-width X baud-rate X send-string-to-terminal X X X X X XProcesses X processp X get-process X get-buffer-process X delete-process X process-status X process-id X process-name X process-command X set-process-buffer X process-buffer X process-mark X set-process-filter X process-filter X set-process-sentinel X process-sentinel X process-kill-without-query X list-processes X start-process X accept-process-output X send-region X send-string X interrupt-process X kill-process X quit-process X stop-process X continue-process X process-send-eof X call-process X call-process-region X XOperating system interface X getenv X load-average X user-login-name X user-real-login-name X user-full-name X system-name X current-time-string X display-time X X pwd X cd X X X kill-emacs X dump-emacs X suspend-emacs X X X compile X grep X shell X shell-command X shell-command-on-region X X rnews X rmail X mail X XPermutted Command Index XSubject Index SHAR_EOF z=`wc -c < 'outline'` if test 9622 -ne $z then echo shar: error transmitting "'outline'" \($z characters read\) fi echo shar: extracting "'tutorial'" '(28064 characters)' if test -f 'tutorial' then echo shar: over-writing existing file "'tutorial'" fi sed 's/^X//' << \SHAR_EOF > 'tutorial' XCopyright (C) 1986 Brian Marick X XHere's a tutorial on customizing Emacs that I put together. I'd like to Xknow if you think the basic approach makes sense, whether the structure of Xthis instance of the approach looks okay, and whether I've picked a good Xexample. Thanks. X X XComments to Brian Marick at one of these addresses: Xmarick@gswd-vms.arpa (arpanet) Xihnp4!uiucdcs!ccvaxa!marick (usenet) X XNOTE: Part of the tutorial describes the code in shell.el. The Xdescriptions are accurate for 18.26 and probably for 18.27, 18.28, ... XThey aren't right for 17.64 -- the most recent general release -- Xbecause the main routine being described was changed. Since the routine Xstill does the same thing, only with rearranged code, you should still be Xable to map the description onto the code. X XThere may be other minor incompatibilities with older versions. X X---------------------------CUT HERE------------------------------------- X@setfilename ../info/customizing-tutorial X@node top, problem, (dir), (dir) X@chapter A Simple Example of Customizing GNU Emacs X@heading Introduction X@subheading What Is This For? XThe purpose of this tutorial is to demonstrate the process of Xcustomization by having the reader work through an example. XIt does not explore any part of GNU Emacs in great detail. X XThere's homework at the end, so that you can apply what you've Xlearned, using a now-familiar chunk of code. (@xref{exercises}) X X@subheading What Is This? XThis file is something like a transcript, with commentary, of a GNU XEmacs customization. It is written somewhat in the style of the GNU XEmacs tutorial, so that you can---and should---follow along and do the Xcustomization yourself. X X@subheading Who Is This For? XThis particular customization was one of the first I did. It is not a Xcontrived example---it's what I actually did, recorded as I did it X(though since modified for new versions of Emacs). It's probably most Xsuited to a person who knows Emacs, C, and some form of Lisp reasonably Xwell. X X@subheading How to Use It? X XFirst, make a private directory and run Emacs there. Run @code{info} and Xselect @code{(customizing-emacs)}. (@xref{(info)} for information about X@code{info}.) (You may also read this tutorial in hardcopy. Hardcopy Xsaves screen space, but you'll still need an info version so that you Xcan copy text from it.)@refill X XRead through the text. Some parts of the text explain Emacs code---read Xthe explanation and also the actual code in another window. Other parts of Xthe text explain what I did to browse through Emacs code. Do the same Xbrowsing---and any other browsing that strikes your fancy. Browsing is Xfundamental to understanding. Other parts of the text have actual large Xchunks of new code, often following motivation and explanation. XCopy that code into the working file (named below); install it and use Xit as explained in the text (usually in paragraphs after the body of the Xcode). X X@subheading Notation XFor the rest of this document, I'll assume that @var{gnu} represents the Xlocation of the Emacs directory on your system. X X@subheading Author Information XThis text is copyright @copyright{} 1986 by Brian Marick. XPermission is granted to make and distribute verbatim copies, Xprovided the copyright notice and this permission notice Xare preserved on all copies. X X@ignore XPermission is granted to process this file through Tex and print the Xresults, provided the printed document carries copying permission Xnotice identical to this one except for the removal of this paragraph X(this paragraph not being relevant to the printed manual). X@end ignore X XPlease send comments on this tutorial to marick@@gswd-vms.arpa or Xihnp4!uiucdcs!ccvaxa!marick. X X X@node problem, solution, top, top X@heading The Problem and a Proposed Solution X XI make heavy use of the @code{csh(1)} @code{cdpath} feature. X@code{cdpath} is a variable used by the shell in this way: X X@comment Quotation seems closest to the effect I want. X@quotation XIf you try to change your working directory to a relative pathname Xthat is not a subdirectory of the current working directory, the @code{csh} Xwill then check if the pathname is a subdirectory of any of the Xdirectories in the @code{cdpath}. X XFor example, suppose you are in @file{/mnt/marick} and your @code{cdpath} contains X@file{/lisp/testing}. If you type @code{cd tests} and @file{/mnt/marick/tests} exists, Xyou will change to that directory. If @file{/mnt/marick/tests} does not Xexist, but @file{/lisp/testing/tests} does, you will change to that directory. X@end quotation X X@code{cdpath} can cause confusion when you use the shell within an Emacs Xbuffer. Emacs calculates the buffer's current working directory by Xtrying to imitate the actions of the directory-changing commands. Since XEmacs doesn't know about the @code{cdpath}, it can't know that @code{cd Xtests} means @code{cd /lisp/testing/tests}. Consequently, after you Xtype @code{cd tests}, Emacs's idea of the current working directory Xdiffers from the shell's. X XOne possible solution would be to let Emacs know about the @code{cdpath}. XHowever, that wouldn't work for another style of directory-changing. XMany people use @code{csh} aliases to change directories. For example, they Xplace the following in their @file{.cshrc} file: X X@example Xalias tests 'cd /lisp/testing/tests' X@end example X Xso that typing @code{tests} to the shell changes to the X@file{/lisp/testing/tests} directory. X XThe most general solution seems to be to have all directory-changing Xcommands and aliases print the directory stack after they do their work: Xthat is, that they all do what @code{dirs(1)} does. @code{pushd} and X@code{popd} already do Xthat; this solution requires that users of @code{cd} alias it with@refill X X@example Xalias cd 'cd \!*; dirs' X@end example X Xand that other special aliases do the similar thing. X X@node solution, shell-send-input, problem, top X@heading Implementing The Solution X XWhat we're concerned with is what Emacs does when it sends a Xdirectory-changing command to the shell, which is surely a special case Xof what it does when sending any command. A first step is to look at Xthe function that @key{RET} invokes when in a shell buffer. X XCall up a shell buffer with @code{M-x shell @key{RET}}. Inside the Xbuffer, type @code{C-h k @key{RET}} to see what function is bound to X@key{RET}. You will see that the function is @code{shell-send-input}. XWe need to find where that function is defined. X XUsing @code{dired} on the GNU Emacs Lisp directory is one way to find Xpossible source files. (We'll see much better ways later.) XDo that by typing @kbd{C-X d}. XAnswer @code{@var{gnu}/lisp/*.el} to the minibuffer prompt.@refill X XBrowing through the directory, we see this line: X X@example X-r--r--r-- 1 marick 13150 Jul 18 15:10 shell.el X@end example X XSuch a file looks a likely candidate, so visit it (by typing @kbd{f} when the Xcursor is on that line). (Don't view it---we'll want to do Xcursor-positioning, and view-mode turns off keys like Control-f and XControl-e.) X X X@node shell-send-input, reading, solution, top X@subheading Explanation of @code{shell-send-input} X X X@code{shell-send-input} is indeed in this file. Find it. XHere (briefly) is what it does: X X@quotation XThe first fourteen or so lines are concerned with acquiring the text to Xsend to the shell. That text is all the text on the current line, less Xthe shell prompt. (There are two cases: if the current line is at the Xend of the buffer, it is newline terminated. If it is within the Xbuffer, it is copied to the end.) X XBefore sending the text to the shell, @code{shell-send-input} Xcalls @code{shell-set-directory}, which must be the code that checks Xfor directory-changing commands. X XNotice that the call to @code{shell-set-directory} is wrapped within a X@code{condition-case} and a @code{save-excursion}. A X@code{condition-case} catches any errors signalled by the enclosed code. XIn this case, an error causes a call to the X@code{shell-set-directory-error-hook}. The default value for that hook Xfunction is @code{ignore}, which silently ignores the error.@refill X XA @code{save-excursion} prevents any changes made to the mark and point Xfrom affecting their values outside the enclosed code. X XWe could, at this point, try to make use of the X@code{shell-set-directory-error-hook} to solve our problem, but instead Xlet's continue on our original course and examine X@code{shell-set-directory}.@refill X XIn general, directory-changing commands manipulate the X@code{shell-directory-stack}. Note that this stack is not the same as Xthe shell's stack (as printed by @code{dirs}). The shell keeps the Xcurrent directory on the stack; Emacs keeps only old directories. X(We must remember to maintain this convention when we make changes.) X XIn particular, the code is split into several cases. X XIf the command matches the @code{shell-popd-regexp} and the popd has no Xarguments, the Emacs @code{cd} function is used to change to the top Xdirectory on the stack and the stack is popped. X XIf the command matches the @code{shell-pushd-regexp}, one of two things Xhappens: X@enumerate X X@item XIf the @code{pushd} has no arguments, that means ''switch the top two Xelements of the shell stack''. That's done by popping and changing to Xthe top directory on the Emacs stack and pushing the previously-current Xdirectory on the stack.@refill X X@item XIf the @code{pushd} has an argument, the argument is read and Xvariables and special metacharacters are expanded to find a Xdirectory to move to. The previously-current directory is Xpushed on the stack, and the current directory is changed. XIf, however, the directory doesn't exist X(function @code{file-directory-p} returns @code{nil}), the XEmacs @code{shell-directory-stack} is not changed, nor is Xthe current directory. (Note that this is what would happen Xif Emacs tried to execute @code{pushd testing} in our stock Xexample.) X X@quotation XTANGENT: the @code{substitute-in-file-name} function is the first one Xwhose purpose hasn't been reasonably obvious. We might want to Xlook at it to see what it does. First, create a tag file in Xthis directory by executing the @code{etags} program with X X@example XM-! etags @var{gnu}/lisp/*.el @key{RET} X@end example X Xthen position the cursor over the use of @code{substitute-in-file-name} Xand type @kbd{Meta-.}. You'll be asked for a tag to find; typing just a Xcarriage return will take you to the definition of X@code{substitute-in-file-name}. Next, you'll be asked which tag table Xto visit---make sure you visit the one you've just created.@refill X XConfusion! The search fails. One possible explanation is that Xthis function is defined in C, not Lisp. We need to make our Xtag table more comprehensive. Here's a way of doing it: X X@example XM-! etags @var{gnu}/lisp/*.el @var{gnu}/src/*.[ch] @key{RET} X@end example X XNow @kbd{M-. @key{RET}} shows the function. Reading X@code{substitute-in-file-name} reveals that it can signal errors, Xwhich explains a need for the condition-case, which Xcatches and (by default) ignores them.@refill X@end example X@end enumerate X XContinuing with the explanation of the function: X XIf the command matches the @code{shell-cd-regexp}, one of two things happens: X@enumerate X X@item XIf the command has no arguments, Emacs changes to the home Xdirectory. Note that the @code{shell-directory-stack} needn't change. X X@item XIf the command has an argument, it is expanded and Emacs changes Xto that directory. X@end enumerate X@end quotation X XReturning to @code{shell-send-input}: after the call to X@code{shell-set-directory}, we notice the three lines of code that Xactually send the command to the shell. X X XIt appears that what we need to do is combine and Xrearrange the code in this way: X X@enumerate X@item XCheck and remember if the command matches any of the three regexps. X@item XSend the command to shell. X@item XIf we saw a matching command, X@enumerate X@item XRead what the shell just printed. X@item XIf it wasn't an error message, X@enumerate X@item XParse the string into a list of strings. X@item XChange directory to the first one and throw it away. X@item XSubstitute and expand the remaining strings. X@item XMake that the shell-directory-stack. X@end enumerate X@end enumerate X@end enumerate X X@node reading, parsing, shell-send-input, top X@subheading Subproblem 1: How to read what the shell prints. X XLooking at the code that sends to the shell, we see that it sends Xthrough @code{last-input-end}. The text between X@code{last-input-end} and a following Xnewline ought to be what we want. The code to extract the shell's output Xought to look something like the code that's already used to extract Xthe argument part of a directory-changing command.@refill X XWe should write this code now, before going on. We begin by editing a Xfile called @file{custom-shell.el}, which will be used for all our changes to XEmacs shell interaction code. We will X@enumerate X X@item Xcopy in the code for @code{shell-send-input} and change its name to X@code{test-shell-send-input}, X X@item Xremove the code that parses directory-changing commands, since we'll Xbe letting the shell do that, X X@item Xand add code that extracts what the shell prints (the @code{dirs}-style Xdirectory stack) and displays it. X@end enumerate X XOne question: how to display what's extracted?---how to debug? XIf you look at the code Xfor @code{shell-send-input}, you'll see that it expects to be run when a X@key{RET} is Xtyped, since it searches for that character. That makes it inconvenient Xto call @code{test-shell-send-input} in any other way.@refill X XAn obvious---and generally useful---thing to do is display Xthe result in the minibuffer. Fortunately, we remember that changing Xthe directory in a shell window already prints the new directory in the Xminibuffer. So we look at (using tags) Xthe @code{cd} function, which leads us to X@code{pwd}, which Xshows us the @code{message} function, which is what we want.@refill X XThe final version looks like this, where lines marked with @code{;NEW} are new. X@group X X@example X(defun test-shell-send-input () X (interactive) X (end-of-line) X (if (eobp) X (progn X (move-marker last-input-start X (process-mark (get-buffer-process (current-buffer)))) X (insert ?\n) X (move-marker last-input-end (point))) X (beginning-of-line) X (re-search-forward shell-prompt-pattern nil t) X (let ((copy (buffer-substring (point) X (progn (forward-line 1) (point))))) X (goto-char (point-max)) X (move-marker last-input-start (point)) X (insert copy) X (move-marker last-input-end (point)))) X (let ((process (get-buffer-process (current-buffer)))) X (send-region process last-input-start last-input-end) X (set-marker (process-mark process) (point)) X (accept-process-output process)) ;NEW X (save-excursion ;NEW X (message "%s" ;NEW X (buffer-substring last-input-end ;NEW X (progn ;NEW X (goto-char last-input-end) ;NEW X (skip-chars-forward "^\n") ;NEW X (point)))))) ;NEW X@end example X X@end group XMost of the NEW lines correspond well to what we planned to do. We Xprint a message from the end of the input through the next newline. The X@code{save-excursion} prevents the changes to the point from contaminating Xwhere the user left the cursor. X XWhat's very different is use of @code{accept-process-output}. In retrospect, Xof course, it's obvious why it's necessary, but finding out in the first Xplace was some work. The reason is that @code{send-region} sends commands to Xthe process but doesn't wait for the process to respond. So if we later Xtried to skip past a line of the shell's output, we'd get a null string, Xbecause there isn't any output yet. @code{accept-process-output} (which we Xdiscovered by looking at @code{send-region} through the tag table and browsing Xthrough the file that contained it) waits for process output. X XNote that the real version of @code{shell-send-input} should only use X@code{accept-process-output} when a directory-changing command has been seen. XWe don't want to block on any arbitrary line of input sent to the Xshell, only on directory-changing commands. X XTry playing around with the code; simulate what's done during debugging. X(For example, remove the call to @code{accept-process-output}.) XTo execute the code, do the following: X@enumerate X X@item XEvaluate the definition with @kbd{C-M-x}. X(Note: @kbd{C-M-x} expects to find @code{(def} at the beginning of the Xfirst line in the function, so remove the leading blanks.) X X@item XCreate a shell, and install this new function as the function bound to X@key{RET} with X X@example XM-x local-set-key @key{RET} @key{RET} test-shell-send-input X@end example X X@item XType things to the shell. (Try @code{echo wombat} and @code{dirs}.) X@end enumerate X X@node parsing, new-shell-send-input, reading, top X@subheading Subproblem 2: Parsing shell output. X XThe only remaining problematic part of our original plan is parsing the Xoutput from the shell into a list of strings. Since strings are a basic Xdatatype, the routines to manipulate them are probably written in C. X XGrepping for string functions with X X@example XM-x grep 'DEFUN.*string' @var{gnu}/src/*.c X@end example X Xyields a quite a number of functions, including X X@example X../src/fns.c:362:DEFUN ("substring", Fsubstring, Ssubstring, 2, 3, 0, X@end example X XBrowsing through @file{fns.c}, we discover a number of useful functions --- Xnotably @code{elt}, to extract characters from a string. With those two Xfunctions and the special form @code{while} that we remember seeing during Xearlier browsing, it's easy to write a function like we need: X X@group X@example X;;; This code expects a string of directories. Each one (including the X;;; last) should be terminated by a single space, in the style of the X;;; csh(1) "dirs" command. That given, it's straightforward to iterate X;;; down the string, extracting substrings at each space and pushing X;;; them on a list. That list (which is now in reverse order) should X;;; have substitute-in-file-name and expand-file-name applied to each X;;; element, to get a list of absolute pathnames without metacharacters. X X(defun parse-and-expand-directories (shell-directory-stack) X (let ((result nil)) X (let ((first-char 0) ; first character of directory. X (pos 0) ; current position rover. X (len (length shell-directory-stack))) ; Stop here. X X (while (< pos len) X (cond ((eql (elt shell-directory-stack pos) ?\ ) X (setq result X (cons (substring shell-directory-stack first-char pos) X result)) X (setq first-char (+ pos 1)))) X (setq pos (+ 1 pos)))) X (mapcar (function (lambda (directory) X (expand-file-name X (substitute-in-file-name directory)))) X (nreverse result)))) X@end example X X@end group XWe install this function with @kbd{C-M-x}, as we did with @code{shell-send-input.} XWe then test it by switching to the @samp{*scratch*} buffer, which is a XLisp-Interaction mode buffer, and typing the following, followed by a X@key{LFD}. X X@example X(parse-and-expand-directories "~ ~wombat /lisp/bin /tmp/$USER ") X@end example X XThe result looks correct: X X@example X("/mnt/marick" "/mnt/wombat" "/lisp/bin" "/tmp/marick") X@end example X X X@node new-shell-send-input, exercises, parsing, top X@subheading Back to the Main Problem: X XWe're now ready to write the new version of @code{shell-send-input}. One Xremaining question is how to detect when the shell signals an error. XNote that we depend on a successful directory-change sending back a Xblank-terminated line. A couple of simple tests show that error Xmessages generally are @i{not} blank terminated. So we'll let the Xpresence or absence of a trailing blank determine whether an error has Xoccurred. (This is, of course, somewhat shaky design---if it proves Xinadequate, we'll do something else.) X XAfter some debugging with a shell window, we arrive at this function: X@group X X@example X;;; New version of shell-send-input. The old version could get X;;; confused about the current working directory. This version X;;; expects directory-changing-commands to print a ''dirs(1)''-style X;;; message after finishing. This function then reads that line, X;;; parses it, and installs it as the value of shell-directory-stack. X;;; Things to know: X;;; 1. We detect errors in the directory-changing command by looking X;;; for a missing space at the end of the output line. Dirs puts one X;;; there, error messages don't generally have one. This is shaky. X;;; 2. There's really no point to having a shell-directory-stack any X;;; more, nor to having three different directory-changing regexps. X;;; These are retained for compatibility, also in case there's a use X;;; for them someday. X X X(defun shell-send-input () X "Send input to subshell. XAt end of buffer, sends all text after last output X as input to the subshell, including a newline inserted at the end. XNot at end, copies current line to the end of the buffer and sends it, Xafter first attempting to discard any prompt at the beginning of the line Xby matching the regexp that is the value of shell-prompt-pattern if Xpossible. This regexp should start with \"^\"." X (interactive) X (end-of-line) X (if (eobp) X (progn X (move-marker last-input-start X (process-mark (get-buffer-process (current-buffer)))) X (insert ?\n) X (move-marker last-input-end (point))) X (beginning-of-line) X (re-search-forward shell-prompt-pattern nil t) X (let ((copy (buffer-substring (point) X (progn (forward-line 1) (point))))) X (goto-char (point-max)) X (move-marker last-input-start (point)) X (insert copy) X (move-marker last-input-end (point)))) X (let ((directory-change-possible X (save-excursion X (progn X (goto-char last-input-start) X (or (looking-at shell-popd-regexp) X (looking-at shell-pushd-regexp) X (looking-at shell-cd-regexp)))))) X ;; Now send the command to the shell. If a directory change is X ;; possible, wait for a reply. X (let ((process (get-buffer-process (current-buffer)))) X (send-region process last-input-start last-input-end) X (set-marker (process-mark process) (point)) X (if directory-change-possible X (accept-process-output process))) X X ;; If we might have changed the directory, parse the shell's answer. X (if directory-change-possible X (let ((shell-answer X (save-excursion X (buffer-substring last-input-end X (progn X (goto-char last-input-end) X (skip-chars-forward "^\n") X (point)))))) X ;; Test to see whether we've been given a directory list or X ;; an error message. Determined by whether there's a trailing X ;; blank on the line. If no error, set up the X ;; shell-directory-stack. X (cond ((eql (elt shell-answer (- (length shell-answer) 1)) X ?\ ) X (setq shell-directory-stack X (parse-and-expand-directories shell-answer)) X (cd (car shell-directory-stack)) X (setq shell-directory-stack X (cdr shell-directory-stack)))))))) X@end example X@end group X XTest @code{shell-send-input}. Notice that @code{shell-send-input} Xdisplays the new directory on the message line. When you try to change Xto a nonsense directory, it displays nothing. You can see that the XEmacs buffer's directory is still the same as the shell's by typing X@code{dirs} to the shell and typing X X@example XC-h v default-directory @key{RET} X@end example X Xto Emacs. X XWhen you're finished testing, place the functions X@code{parse-and-expand-directories} and @code{shell-send-input} into the Xfile @file{custom-shell.el}, compile it with X X@example XM-x byte-compile-file @key{RET} X@end example X Xload it with X X@example XM-x load-library @key{RET} custom-shell @key{RET} X@end example X Xand try it out some more. It still works. X XNow we need only install the file in some standard directory and find a Xway to load it. The easiest thing to do is just load @file{shell} and X@file{custom-shell} in our @file{.emacs} file. (Slightly better is to Xjust load @file{custom-shell} and have @file{custom-shell.el} include Xthe line X X@example X(require 'shell) X@end example X Xwhich causes the loading of @file{shell.elc}.) X XAlternatively, we can only load @file{custom-shell.el} when a shell process is Xstarted. This is done with the following @code{shell-mode-hook}: X X@example X(setq shell-mode-hook X '(lambda () X (require 'custom-shell))) X@end example X XWe use @code{require} instead of @code{load} because the X@code{shell-mode-hook} is executed whenever we switch to the shell Xbuffer with X X@example XM-x shell X@end example X XWe don't want to reload it quite so often. Note that we must put a X X@example X(provide 'custom-shell) X@end example X Xin @file{custom-shell.el}. X X@node exercises, customization-structure, new-shell-send-input, top X@heading Exercises X X@enumerate X X@item XThere is a flaw in the shell code. Suppose, for example, that XI have @code{b} aliased to @code{popd}. What happens when I type X@example Xbm <pattern> <some-enormous-set-of-files> X@end example X(@code{bm} is a fast string-search program.) X XYou will see that Emacs waits for the command to complete, thinking Xit's a directory-changing command. Fix this problem. X X@item XWhat happens if there's extra white space between the prompt and the Xcommand? Fix this problem. X X@item XFind a better way to detect shell error messages. X X@item XOur implementation assumes that @code{accept-process-input} will return Xa complete line. What if it doesn't? X X@item XNow that you're familiar with the shell command, give users the Xability to easily create multiple shell buffers. (For example, Xthe @code{shell} command might take a prefix argument, or it might Xprompt for the name of a shell.) X@end enumerate X X@node customization-structure, top, exercises, top X@heading A General Problem X XNow that you've finished this customization, what do you do with Xit? I think that's a general problem: often you want to modify Xparticular functions within a standard Emacs Lisp file, but you Xdon't want to change that file. Neither do you want to copy the Xentire standard file, since that would isolate you from further Xchanges to it, changes that might, for example, fix bugs. X XA solution might be to have the new file @dfn{shadow} the old one in Xthe following way: X X@enumerate X@item XPut the new file in a new directory. Give it the same name Xas the shadowed file. It will now be loaded in preference to Xthe old file if you have the new directory early in the @code{load-path}. X X@item XTo avoid having to include all of the old file in the new one, arrange for Xthe new file to load the old file as it itself is being loaded. X@end enumerate X XDick King has suggested another way to accomplish much the same thing. XHe suggests that @code{defun} come in two versions, aggressive and Xpassive. Aggressive @code{defuns} behave as @code{defun} does now. X(That is, a new defun overrides an old one with the same name.) Passive X@code{defuns}, when read from a file, would not override old definitions. X XShadowing changes could be made with aggressive @code{defuns}. XThus, they would be immune to load order---we would not have to Xcarefully arrange things so that our version of @code{shell-send-input} Xis loaded only after all of @file{shell.elc} is loaded. X X@bye X SHAR_EOF z=`wc -c < 'tutorial'` if test 28064 -ne $z then echo shar: error transmitting "'tutorial'" \($z characters read\) fi # End of shell archive exit 0