From 3067841f3d5aab05d820a866d56ea0db5848e553 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 25 Jun 1992 12:00:00 +0200 Subject: [PATCH] S-mode 3.4 OVERVIEW ======== * Works with version 3.0 S * Command-line completion of S object names * Recognition of attached data frames * Dedicated S Help mode * Tek graphics support * Several bugfixes and code cleanups * Texinfo documentation CHANGES ======= For current users of S-mode, here are some of the incompatible changes and features new to version 3.3 of S-mode: * Command-line completion of object names, and faster completion in other situations. * `Hot Keys' for the commonly-used functions `objects()', `search()' and `attach()' and a facility to add your own hot keys with keyboard macros. * Simultaneous multiple function editing, with integrated error-checking and parsing. Mnemonic names for edit buffers. * Debugging features: facility for stepping through S code and evaluating portions of code with the output appearing as if the commands has been typed in manually. * S can now be run from a different directory each session. * A dedicated mode for viewing S help files. Individual help buffers are maintained for quick repeated access. Completion for help files without a corresponding object. * Facility for maintaining organised backups of S source code. * Indenting and formatting commands for editing S source code. * Special handling of the S graphics facilities, including an experimental Tek graphics mode. * Better handling of temporary files and buffers. * Some keybindings have changed to conform to GNU guidelines. Here are the changes to the keybindings from version 2.1: INFERIOR-S-MODE: C-c h S-display-help-on-object MOVED TO C-c C-h [*] C-c d S-dump-object-into-scratch MOVED TO C-c C-d [*] C-c l S-load-file MOVED TO C-c C-l [*] C-c C-o comint-kill-output REDEFINED AS S-kill-output C-c C-z comint-stop-subjob REDEFINED AS S-abort S-MODE: C-c h S-display-help-on-object MOVED TO C-c C-h [*] C-c l S-load-file MOVED TO C-c C-l [*] C-c z switch-to-S MOVED TO C-c C-y [*] C-c C-k S-eval-line-and-go MOVED TO C-c M-j [*] C-c k S-eval-line MOVED TO C-c C-j [*] C-c C-e S-eval-function-and-go MOVED TO C-c M-f REDEFINED AS S-execute-in-tb C-c e S-eval-function MOVED TO C-c C-f [*] C-c C-b S-eval-buffer-and-go MOVED TO C-c M-b C-c b S-eval-buffer MOVED TO C-c C-b [*] C-c C-r S-eval-region-and-go MOVED TO C-c M-r C-c r S-eval-region MOVED TO C-c C-r [*] If your fingers insist on using the old bindings, place the following piece of code in your .emacs to emulate the bindings marked [*] above: (setq S-mode-load-hook '(lambda nil (define-key inferior-S-mode-map "\C-ch" 'S-display-help-on-object) (define-key inferior-S-mode-map "\C-cl" 'S-load-file) (define-key inferior-S-mode-map "\C-cd" 'S-dump-object-into-edit-buffer) (define-key S-mode-map "\C-ch" 'S-display-help-on-object) (define-key S-mode-map "\C-cl" 'S-load-file) (define-key S-mode-map "\C-cz" 'S-switch-to-S) (define-key S-mode-map "\C-c\C-k" 'S-eval-line-and-go) (define-key S-mode-map "\C-ck" 'S-eval-line) (define-key S-mode-map "\C-ce" 'S-eval-function) (define-key S-mode-map "\C-cb" 'S-eval-buffer) (define-key S-mode-map "\C-cr" 'S-eval-region))) However, I do encourage you to become familiar with the new bindings. * General code cleanups and optimizations. In particular, a number of variable and function names have been changed. You may need to modify any hooks you used in version 2.1. The changes are: Before version 3.3 Version 3.3 ------------------ ----------- explicit-Splus-args inferior-Splus-args make-S-comint inferior-S-make-comint S-send-input inferior-S-send-input S-get-old-input inferior-S-get-old-input wait-for-S-prompt inferior-S-wait-for-prompt S-dump-object-into-scratch S-dump-object-into-edit-buffer find-S-object-default S-read-object-name-default find-S-object S-read-object-name get-S-search-list S-get-search-list get-S-object-list-r S-get-object-list-r get-S-object-list S-get-object-list command-to-S S-command beginning-of-S-function S-beginning-of-function end-of-S-function S-end-of-function extract-word-name S-extract-word-name switch-to-S S-switch-to-S switch-to-end-of-S S-switch-to-end-of-S make-S-function S-make-function electric-S-brace S-electric-brace calculate-S-indent S-calculate-indent mark-S-function S-mark-function indent-S-exp S-indent-exp set-S-style S-set-style find-S-help-file S-find-help-file get-S-help-files-list S-get-help-files-list nuke-S-help-bs S-nuke-help-bs default-S-style S-default-style (some of these symbols were not present in version 2.1 of S.el, but have been used in `private' releases since 2.1) In addition, many of the internal functions have been modified in implementation and/or usage. See S.el for more information. --- CHANGES | 124 +++ README | 75 ++ S-mode.texinfo | 2223 +++++++++++++++++++++++++++++++++++++++++ S-tek.el | 217 ++++ S.el | 2396 ++++++++++++++++++++++++++++++++++++++------- comint-extra.el | 74 ++ comint-isearch.el | 291 ++++++ comint.el | 1386 ++++++++++++++++++++++++++ 8 files changed, 6405 insertions(+), 381 deletions(-) create mode 100644 CHANGES create mode 100644 README create mode 100644 S-mode.texinfo create mode 100644 S-tek.el create mode 100644 comint-extra.el create mode 100644 comint-isearch.el create mode 100644 comint.el diff --git a/CHANGES b/CHANGES new file mode 100644 index 000000000..a06c712b7 --- /dev/null +++ b/CHANGES @@ -0,0 +1,124 @@ +This file documents the changes from version 2.1 of S.el (also known +as gnuemacs3 on Statlib) and the current version. + +For current users of S-mode, here are some of the incompatible +changes and features new to version 3.3 of S-mode: + + * Command-line completion of object names, and faster completion in + other situations. + + * `Hot Keys' for the commonly-used functions `objects()', + `search()' and `attach()' and a facility to add your own hot keys + with keyboard macros. + + * Simultaneous multiple function editing, with integrated + error-checking and parsing. Mnemonic names for edit buffers. + + * Debugging features: facility for stepping through S code and + evaluating portions of code with the output appearing as if the + commands has been typed in manually. + + * S can now be run from a different directory each session. + + * A dedicated mode for viewing S help files. Individual help + buffers are maintained for quick repeated access. Completion for + help files without a corresponding object. + + * Facility for maintaining organised backups of S source code. + + * Indenting and formatting commands for editing S source code. + + * Special handling of the S graphics facilities, including an + experimental Tek graphics mode. + + * Better handling of temporary files and buffers. + + + * Some keybindings have changed to conform to GNU guidelines. + +Here are the changes to the keybindings from version 2.1: + +INFERIOR-S-MODE: + +C-c h S-display-help-on-object MOVED TO C-c C-h [*] +C-c d S-dump-object-into-scratch MOVED TO C-c C-d [*] +C-c l S-load-file MOVED TO C-c C-l [*] +C-c C-o comint-kill-output REDEFINED AS S-kill-output +C-c C-z comint-stop-subjob REDEFINED AS S-abort + +S-MODE: + +C-c h S-display-help-on-object MOVED TO C-c C-h [*] +C-c l S-load-file MOVED TO C-c C-l [*] +C-c z switch-to-S MOVED TO C-c C-y [*] +C-c C-k S-eval-line-and-go MOVED TO C-c M-j [*] +C-c k S-eval-line MOVED TO C-c C-j [*] +C-c C-e S-eval-function-and-go MOVED TO C-c M-f + REDEFINED AS S-execute-in-tb +C-c e S-eval-function MOVED TO C-c C-f [*] +C-c C-b S-eval-buffer-and-go MOVED TO C-c M-b +C-c b S-eval-buffer MOVED TO C-c C-b [*] +C-c C-r S-eval-region-and-go MOVED TO C-c M-r +C-c r S-eval-region MOVED TO C-c C-r [*] + +If your fingers insist on using the old bindings, place the following +piece of code in your .emacs to emulate the bindings marked [*] above: + +(setq S-mode-load-hook + '(lambda nil + (define-key inferior-S-mode-map "\C-ch" 'S-display-help-on-object) + (define-key inferior-S-mode-map "\C-cl" 'S-load-file) + (define-key inferior-S-mode-map "\C-cd" 'S-dump-object-into-edit-buffer) + (define-key S-mode-map "\C-ch" 'S-display-help-on-object) + (define-key S-mode-map "\C-cl" 'S-load-file) + (define-key S-mode-map "\C-cz" 'S-switch-to-S) + (define-key S-mode-map "\C-c\C-k" 'S-eval-line-and-go) + (define-key S-mode-map "\C-ck" 'S-eval-line) + (define-key S-mode-map "\C-ce" 'S-eval-function) + (define-key S-mode-map "\C-cb" 'S-eval-buffer) + (define-key S-mode-map "\C-cr" 'S-eval-region))) + +However, I do encourage you to become familiar with the new bindings. + + + * General code cleanups and optimizations. + +In particular, a number of variable and function names have been +changed. You may need to modify any hooks you used in version 2.1. The +changes are: + +Before version 3.3 Version 3.3 +------------------ ----------- +explicit-Splus-args inferior-Splus-args +make-S-comint inferior-S-make-comint +S-send-input inferior-S-send-input +S-get-old-input inferior-S-get-old-input +wait-for-S-prompt inferior-S-wait-for-prompt +S-dump-object-into-scratch S-dump-object-into-edit-buffer +find-S-object-default S-read-object-name-default +find-S-object S-read-object-name +get-S-search-list S-get-search-list +get-S-object-list-r S-get-object-list-r +get-S-object-list S-get-object-list +command-to-S S-command +beginning-of-S-function S-beginning-of-function +end-of-S-function S-end-of-function +extract-word-name S-extract-word-name +switch-to-S S-switch-to-S +switch-to-end-of-S S-switch-to-end-of-S +make-S-function S-make-function +electric-S-brace S-electric-brace +calculate-S-indent S-calculate-indent +mark-S-function S-mark-function +indent-S-exp S-indent-exp +set-S-style S-set-style +find-S-help-file S-find-help-file +get-S-help-files-list S-get-help-files-list +nuke-S-help-bs S-nuke-help-bs +default-S-style S-default-style + +(some of these symbols were not present in version 2.1 of S.el, but +have been used in `private' releases since 2.1) + +In addition, many of the internal functions have been modified in +implementation and/or usage. See S.el for more information. diff --git a/README b/README new file mode 100644 index 000000000..fd22b4e01 --- /dev/null +++ b/README @@ -0,0 +1,75 @@ +S-mode version 3.4 +------------------ + +Please note: the filenames mentioned below may be compressed at this +site (such files will have an extra extension of `.Z'). Such files +need to be transferred in ftp BINARY mode and uncompressed (e.g. with +the Unix "uncompress" utility) at your site. Tar files (ending in +`.tar') must also be transferred in BINARY mode. + +This is version 3.4 of the S-mode distribution. You should get +S-mode3.4.tar, which contains the following files: + +README This file +CHANGES Changes from version 2.1 +S.el (version 3.4) Emacs-lisp code for S-mode +S-tek.el (version 1.0) Graphics engine for Tek terminals +comint.el The comint package +comint-extra.el Additions to the comint package +comint-isearch.el Incremental search for comint command history +S-mode.texinfo (v 1.20) Documentation for S-mode + +(You can extract these files with the Unix command "tar xvf S-mode3.4.tar") + + +INSTALLING THE DOCUMENTATION +---------------------------- + +Within Emacs, type "C-h i m Texinfo RET" for information on +installing and reading Texinfo files. If you can't do this then you +can get one of the following files from attunga.stats.adelaide.edu.au +in directory pub/S-mode by anonymous ftp: + +S-mode.info For installing in your info tree +S-mode.ps Postscript version of the documentation +S-mode.dvi DVI version of the documentation + +INSTALLING S-MODE +----------------- + +Create a directory (say, `~/elisp') to place the Emacs-Lisp files. +Copy `S.el', `comint.el' and `comint-extra.el' to that directory, and +add the lines + + (setq load-path (cons (expand-file-name "~/elisp") load-path)) + (autoload 'S "S" "Run an inferior S process" t) + (autoload 's-mode "S" "Mode for editing S source" t) + +to your `.emacs' file. You can byte-compile the `.el' files if you +wish. See the documentation and the `S.el' file for more details --- +in particular, if you use a non-standard prompt or if the command to +run S is not `Splus' then you have more to do. + +CHANGES FROM VERSION 2.1 +------------------------ + +See the file CHANGES in the S-mode distribution. + +BUG REPORTS, ETC +---------------- + +Until the end of August 1992, please report bugs to me at +dsmith@stats.adelaide.edu.au. After this date, mail to that address +will not be answered for some time; please contact Frank Ritter +(Frank_Ritter@SHAMO.SOAR.CS.CMU.EDU) or any of the other authors then +(please CC: to me as well though -- you never know, I might just +answer!) Comments, suggestions, words of praise and large cash +donations are also more than welcome. + + +Enjoy! + +-- +Dave Smith +Department of Statistics +University of Adelaide, South Australia diff --git a/S-mode.texinfo b/S-mode.texinfo new file mode 100644 index 000000000..0008e424f --- /dev/null +++ b/S-mode.texinfo @@ -0,0 +1,2223 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename S-mode.info +@settitle S-mode +@comment %**end of header (This is for running Texinfo on a region.) + +@synindex pg vr + +@node Top, Introduction, (dir), (dir) +@comment node-name, next, previous, up + +@ifinfo +@unnumbered S-mode + +This file documents @code{S.el}, a GNU Emacs mode for running +@code{Splus} in a buffer. + +This info is current to Version 3.4 of @code{S.el}. + +Author: David Smith (dsmith@@stats.adelaide.edu.au), Department of +Statistics, University of Adelaide, South Australia. + +Info version: 1.20 +@end ifinfo + +@titlepage +@sp5 +@center @titlefont{S-mode} +@center version 3.4 +@sp2 +@center An GNU Emacs package +@center for interacting with the +@center S/Splus statistical software packages +@sp7 +@center David Smith +@center (@code{dsmith@@stats.adelaide.edu.au}) +@center Department of Statistics +@center University of Adelaide, South Australia +@sp7 +@center Documentation version: 1.20 +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1992 Department of Statistics, University of Adelaide. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. +@end titlepage + +@menu +* Introduction:: Overview of features provided by this package. +* Starting Up:: Starting the S process +* Entering commands:: Interacting with the process buffer. +* Editing:: How to create or edit S objects or functions. +* Help:: Reading help files in S-mode +* Graphics:: Using graphics with S-mode +* Bugs:: Known bugs in S-mode +* Installation:: Installing S-mode on your system +* Customization:: Customizing S-mode +* Concept Index:: +* Variable and command index:: + + --- The Detailed Node Listing --- + +Introduction to S-mode + +* Credits:: Authors of and contributors to S-mode +* Latest version:: Getting the latest version of S-mode +* New features:: Changes from version 2.1 +* Manual:: How to read this manual + +Interacting with the S process + +* Command-line editing:: Entering commands and fixing mistakes +* Completion:: Completion of object names +* Process buffer motion:: Moving through the process buffer +* Command History:: Command History +* Hot keys:: Hot keys for common commands +* Other inferior-S-mode commands:: Other commands provided by inferior-S-mode + +Editing S functions + +* Edit buffer:: The edit buffer +* Modification:: Modification flags in edit buffers +* Source Files:: Maintaining S source files +* Source Directories:: Names and locations of dump files +* Error Checking:: Detecting and correcting errors + +The edit buffer + +* Evaluating code:: Sending code to the S process +* Indenting:: Indenting and formatting S code +* Other edit buffer commands:: Commands for motion, completion and more + +Using graphics with S-mode + +* printer:: The printer() graphics driver +* tek4014:: The tek4014() graphics driver +* X11:: The X11() (and other X-windows based) driver + +Installing S-mode on your system + +* System dependent:: Other variables you may need to change + +Customizing S-mode + +* Variables:: Variables for customization +* Hooks:: Customizing S-mode with hooks +* Keybindings:: Changing the default S-mode keybindings + +Variables for customization + +* Variables for starting S:: Variables for starting S +* Dump file variables:: Variables for dump files +* Indentation variables:: Variables controlling indentation +* Variables controlling interaction:: Variables controlling interaction with the S process +@end menu + +@node Introduction, Starting Up, Top, Top +@comment node-name, next, previous, up +@chapter Introduction to S-mode +@cindex introduction + +The S and Splus packages provide sophisticated statistical and graphical +routines for manipulating data. The S-mode package provides useful +routines for making the use of these packages much easier. + +A bit of notation before we begin. I will refer to both the `new S' +package (as described in Becker, Chambers and Wilks, @cite{The New S +Language: A programming environment for data analysis and graphics}) and +`Splus' (an enhanced version of new S from Statsci) simply by +``S''. The interface which is used to run S under Emacs (which this manual +documents) will be referred to as ``S-mode'', which should not be +confused with the GNU Emacs major mode @code{S-mode} which is used for +editing S source. Finally, the GNU Emacs Lisp source code in which this +package is defined will be referred to as @file{S.el}. + +@cindex interactive use of S +@cindex using S interactively +For exclusively interactive users of S, S-mode provides a number of +features to make life easier. There is an easy-to-use command history +mechanism, including a quick prefix-search history. To reduce typing, +command-line completion is provided for all S objects and ``hot keys'' +are provided for common S function calls. Help files are easily +accessible, and a paging mechanism is provided to view them. Finally, an +incidental (but very useful) side-effect of S-mode is that a transcript of +your session is kept for later saving or editing. +@cindex transcripts of S sessions +No special knowledge of Emacs is necessary when using S interactively +under S-mode. + +@cindex programming in S +For those that use S in the typical edit-test-revise cycle when +programming S functions, S-mode provides for editing of S functions in +Emacs edit buffers. Unlike the typical use of S where the editor is +restarted every time an object is edited, S-mode uses the current Emacs +session for editing. In practical terms, this means that you can edit +more than one function at once, and that the S process is still +available for use while editing. Error checking is performed on +functions loaded back into S, and a mechanism to jump directly to the +error is provided. S-mode also provides for maintaining text versions of +your S functions in specified source directories. + +@menu +* Credits:: Authors of and contributors to S-mode +* Latest version:: Getting the latest version of S-mode +* New features:: Changes from version 2.1 +* Manual:: How to read this manual +@end menu + +@node Credits, Latest version, , Introduction +@comment node-name, next, previous, up +@section Authors of and contributors to S-mode + +S-mode is based on Olin Shivers' excellent comint package (which comes +@cindex comint +@cindex authors +@cindex credits +with the S-mode distribution). The original version of S-mode was +written by Doug Bates (@code{bates@@stat.wisc.edu}) and Ed Kademan +(@code{kademan@@stat.wisc.edu}). Frank Ritter +(@code{ritter@@psy.cmu.edu}) then merged this version with his own S +mode to form @code{S.el} version 2.1. + +Version 2.1 of S.el was then updated and expanded by David Smith +to form version 3.4. Most +bugs have now been fixed (and several new ones introduced) and many new +features have been added. Thanks must go to the beta testers for version +3.4: + +@itemize @bullet +@item +@code{S-eval-line-and-next-line} is based in an idea by Rod Ball. + +@item +Thanks to Doug Bates for many useful suggestions. + +@item +@code{comint-isearch} was written by Terry Glanfield +(@code{tg.southern@@rxuk.xerox.com}). + +@item +Thanks to Martin Maechler for reporting and fixing bugs. + +@item +Thanks to Frank Ritter for updates from the previous version, +suggestions and invaluable comments on the manual. + +@item +Thanks to Ken'ichi Shibayama for his +excellent indenting code, and many comments and suggestions. + +@item +Thans to Bob Stine for testing an early version of the Tek graphics +support. +@end itemize + +@node Latest version, New features, Credits, Introduction +@comment node-name, next, previous, up +@section Getting the latest version of S-mode + +The latest version of S-mode is always available for anonymous FTP from + +@display +@code{attunga.stats.adelaide.edu.au} +@end display + +@noindent +in the directory @file{pub/S-mode}. +Check the @code{README} file first to see which files you need. S-mode +is also available from the Emacs-Lisp archive on +@code{archive.cis.ohio-state-edu} --- retrieve + +@display +@file{pub/gnu/emacs/elisp-archive/README} +@end display + +@noindent +for information on the +archive. The latest version is also available from Statlib by sending a +blank message with subject ``send index from S'' to +@code{statlib@@stat.cmu.edu}, and following the directions from there. + +@node New features, Manual, Latest version, Introduction +@comment node-name, next, previous, up +@section Changes from version 2.1 + +For current users of S-mode, here are some of the incompatible changes +and features new to version 3.3 of +S-mode: +@cindex features of S-mode +@cindex new features +@cindex changes to S-mode + +@itemize @bullet +@item +Command-line completion of object names, and faster completion in other +situations. + +@item +`Hot Keys' for the commonly-used functions @code{objects()}, +@code{search()} and @code{attach()} and a facility to add your own hot +keys with keyboard macros. + +@item +Simultaneous multiple function editing, with integrated error-checking +and parsing. Mnemonic names for edit buffers. + +@item +Debugging features: facility for stepping through S code and evaluating +portions of code with the output appearing as if the commands has been +typed in manually. + +@item +S can now be run from a different directory each session. + +@item +A dedicated mode for viewing S help files. Individual help buffers are +maintained for quick repeated access. Completion for help files +without a corresponding object. + +@item +Facility for maintaining organised backups of S source code. + +@item +Indenting and formatting commands for editing S source code. + +@item +Special handling of the S graphics facilities, including an +experimental Tek graphics mode. + +@item +Better handling of temporary files and buffers. + +@item +Some keybindings have changed to conform to GNU guidelines. + +@item +General code cleanups and optimizations. +@end itemize + +@node Manual, , New features, Introduction +@comment node-name, next, previous, up +@section How to read this manual + +If S-mode has already been installed on your system, the next chapter +has details on how to get started using S under S-mode. + +If you need to install S-mode, read @ref{Installation} for details on +what needs to be done before proceeding to the next chapter. + +@ref{Customization} provides details of user variables you can change to +customize S-mode to your taste, but it is recommended that you defer +this section until you are more familiar with S-mode. + +Don't forget that this manual is not the only source of information +about S-mode. In particular, the mode-based online help (obtained by +pressing @kbd{C-h m} when in the process buffer, edit buffer or help +buffer) is quite useful. However the best source of information is, as +always, experience --- try it out! + +@node Starting Up, Entering commands, Introduction, Top +@comment node-name, next, previous, up +@chapter Starting the S process +@cindex starting S-mode +@cindex running S + +To start an S session, simply type @kbd{M-x S RET}, i.e. press +@key{ESC}, then @key{x}, then capital @key{S} and then the @key{RETURN} key. +@pindex S + +@cindex S process directory +@cindex starting directory +@cindex working directory +@cindex directories +If the variable @code{S-ask-for-S-directory} +@vindex S-ask-for-S-directory +has a non-@code{nil} value, you will be prompted with +@example +From which directory? +@end example +@noindent +with a default value chosen on the basis of the variable +@code{S-directory}. +@vindex S-directory +The S program will be run from the directory you +specify at this point, that is S will use the @file{.Data} subdirectory +@cindex @file{.Data} directory +of this directory (if it exists.) Using this facility it is possible to +have a number of distinct S directories for separate projects, etc. If +the value of @code{S-ask-for-S-directory} is @code{nil}, the S program +will be run from the directory specified by @code{S-directory} (which +defaults to your home directory). + +Next, if the value of @code{S-ask-about-display} +@vindex S-ask-about-display +is non-@code{nil} you will be presented with the prompt +@example +Which X-display? +@end example +@noindent +The value you enter here will be used as the value of the @code{DISPLAY} +environment variable +@cindex DISPLAY environment variable +of the S process for use with the X windowing system. +@cindex X windows +Unless this variable is set correctly, S commands such as @code{X11()} +@pindex X11() +or @code{help.start()} +@pindex help.start() +will not work. Completion is provided on the basis of the +variable @code{X-displays-list}. +@vindex S-displays-list + +Once these questions are answered (if they are asked at all) the +program defined by @code{inferior-S-program} will be executed with +@vindex inferior-S-program +arguments specified by @code{explicit-@var{S_program_name}-args}. +@cindex arguments to S program +You will be popped into a buffer +@cindex S process buffer +@cindex process buffer +with name @samp{*S*} which will be used +for interacting with the S process, and you can start entering commands. + +@node Entering commands, Editing, Starting Up, Top +@comment node-name, next, previous, up +@chapter Interacting with the S process +@cindex entering commands +@cindex commands +@cindex sending input + +The primary function of the S-mode package is to provide an easy-to-use +front end to the S interpreter. This is achieved by running the S +process from within an Emacs buffer, so that the Emacs editing commands +are available to correct mistakes in commands, etc. A sophisticated +command history and recall mechanism is also provided, thanks to the +@code{comint} package. Command-line completion of S objects and a number +of `hot keys' for commonly-used S commands are also provided for ease of +typing. + +@menu +* Command-line editing:: Entering commands and fixing mistakes +* Completion:: Completion of object names +* Process buffer motion:: Moving through the process buffer +* Command History:: Command History +* Hot keys:: Hot keys for common commands +* Other inferior-S-mode commands:: Other commands provided by inferior-S-mode +@end menu + +@node Command-line editing, Completion, , Entering commands +@comment node-name, next, previous, up +@section Entering commands and fixing mistakes +@cindex command-line editing + +Sending a command to the S process is as simple as typing it in and +pressing the @key{RETURN} key: + +@itemize @bullet +@item +@kbd{RET} (@code{inferior-S-send-input}) @* +@pindex inferior-S-send-input +Send the command on the current line to the S process. +@end itemize + +If you make a typing error before pressing @kbd{RET} all the usual Emacs +editing commands are available to correct it (see @ref{Basic, Basic, +Basic editing commands, emacs, The GNU Emacs Reference Manual}.) Once +the command has been corrected you can press @key{RETURN} (even if the +cursor is not at the end of the line) to send the corrected command to +the S process. + +S-mode provides two other commands which are useful for fixing mistakes: + +@itemize @bullet +@item +@kbd{C-c C-w} (@code{backward-kill-word}) @* +@pindex backward-kill-word +Deletes the previous word (such as an object name) on the command line. + +@item +@kbd{C-c C-u} (@code{comint-kill-input}) @* +@pindex comint-kill-input +Deletes everything from the prompt to point. Use this to abandon a +command you have not yet sent to the S process. +@end itemize + +Finally, the beginning-of-line command (@kbd{C-a}) has been slightly +redefined to leave you at the start of the current command instead: + +@itemize @bullet +@item +@kbd{C-a} (@code{comint-bol}) @* +@pindex comint-bol +Move to the beginning of the line, and then skip forwards past the +prompt, if any. +@end itemize + +@node Completion, Process buffer motion, Command-line editing, Entering commands +@comment node-name, next, previous, up +@section Completion of object names +@cindex completion of object names +@cindex command-line completion + +In the process buffer, @key{TAB} only inserts a @samp{TAB} character +when the cursor is not following an object name. Otherwise, +the @key{TAB} key is for completion: + +@itemize @bullet +@item +@kbd{TAB} (@code{S-complete-object-name}) @* +@pindex S-complete-object-name +Complete the S object name before point. +@end itemize + +When the cursor is just after a partially-completed object name, +pressing @key{TAB} provides completion in a similar fashion to +@code{tcsh} +@cindex tcsh +except that completion is performed over S object names instead of file +names. S-mode maintains a list of all objects known to S at any given +time, which basically consists of all objects (functions and datasets) +in every attached +directory listed by the @code{search()} command +@pindex search() +along with the component objects of attached data frames +@cindex data frames +(if your version of S supports them). + +For example, consider the three functions (available in Splus version +3.0) called @code{binomplot()}, @code{binom.test()} and +@code{binomial()}. Typing @kbd{bin TAB} after the S prompt will insert +the characters @samp{om}, completing the longest prefix (@samp{binom}) +which distinguishes these three commands. Pressing @kbd{TAB} once more +provides a list of the three commands which have these prefix, allowing +you to add more characters (say, @samp{.}) which specify the function +you desire. After entering more characters pressing @kbd{TAB} yet again +will complete the object name up to uniqueness, etc. If you just wish to +see what completions exist without adding any extra characters, pass a +prefix command to @code{S-complete-object-name} with @kbd{C-u TAB}. + +S-mode automatically keeps track of any objects added or deleted to the +system to make completion as accurate as possible. As long as the +command that changed the search list matched @code{S-change-sp-regex}, +@vindex S-change-sp-regex +when a directory or data frame is attached, the objects associated with +it immediately become available for a completion; when detached +completion is no longer available on those objects. Efficiency is gained +by maintaining a cache of objects currently known to S; when a new +object becomes available or is deleted, only one component of the cache +corresponding to the associated directory is refreshed. The command +@kbd{M-x S-resynch} +@pindex S-resynch +forces the @emph{entire} cache to be refreshed --- use this command +whenever S-mode gets confused about completion. +@cindex completion, not working on data frames +One warning: S @emph{never} automatically refreshes its idea of the +components of attached data frames; +if the names of the components of a data frame are modified +during an S session, S-mode will not recognise any new components (or +ignore any deleted +components) until the command @kbd{M-x S-resynch} +is issued. + +S-mode also provides completion over the components of named lists +accessed using the @samp{$} notation, to any level of nested lists. Such +information is never cached, and so is guaranteed to always be correct. +This feature is particularly useful for checking what components of a +list object exist while partway through entering a command: simply type +the object name and @samp{$} and press @kbd{TAB} to see the names of +existing list components for that object. +@cindex lists, completion on +@cindex completion on lists + +@node Process buffer motion, Command History, Completion, Entering commands +@comment node-name, next, previous, up +@section Moving through the process buffer + +Most of the time, the cursor spends most of its time at the bottom of +the S process buffer, entering commands. Sometimes, however, we want to +move back up through the buffer, to look at the output from previous +commands for example. + +Viewing the output of the command you have just entered is a common +occurence and S-mode provides a number of facilities for doing this. +Within the S process buffer, the variable @code{scroll-step} +@vindex scroll-step +is set to 4 (you can redefine this using @code{inferior-S-mode-hook} +@vindex inferior-S-mode-hook +if you wish - @pxref{Hooks},) so that the cursor is usually near the +bottom of the window. Longish command outputs may cause S to place the +cursor at the middle of the window, however, making the first part of +the output hidden above the top of the window. If this happens, you can +use the command + +@itemize @bullet +@item +@kbd{C-c C-v} (@code{S-view-at-bottom}) @* +@pindex S-view-at-bottom +Move to the end of the buffer, and place cursor on bottom line of +window. +@end itemize + +@noindent +will make more of the last output visible. If the first part of the +output is still obscured, use +@cindex reading long command outputs + +@itemize @bullet +@item +@kbd{C-c C-r} (@code{comint-show-output}) @* +@pindex comint-show-output +Moves cursor to the previous command line and +and places it at the top of the window. +@end itemize + +@noindent +to view it. Finally, if you want to discard the last command output +altogether, use + +@itemize @bullet +@item +@kbd{C-c C-o} (@code{S-kill-output}) @* +@pindex S-kill-output +@cindex deleting output +@pindex comint-show-output +Deletes everything from the last command to the current +prompt. +@end itemize + +@noindent +to kill it. + +If you want to view the output from more historic commands than the +previous command, commands are also provided to move backwards and +forwards through previously entered commands in the process buffer: + +@itemize @bullet +@item +@kbd{M-P} (@code{comint-msearch-input}) @* +@pindex comint-msearch-input +Moves point to the preceding command in the process buffer. + +@item +@kbd{M-N} (@code{comint-psearch-input}) @* +@pindex comint-psearch-input +Moves point to the next command in the process buffer. + +@item +@kbd{C-c C-b} (@code{comint-msearch-input-matching}) @* +@pindex comint-msearch-input-matching +Prompts for a string and jump to the previous command you +entered which matched that string. +@end itemize + +When the cursor is not after the current prompt, the @key{RETURN} key +has a slightly different behaviour than usual. Pressing @kbd{RET} on any +line containing a command that you entered (i.e. a line beginning with a +prompt) sends that command to the S process once again. This even works +if the current line is a continuation line (i.e. the prompt is @samp{+ } +instead of @samp{> }) --- in this case all the lines that form the +multi-line command are concatenated together and the resulting command +is sent to the S process (currently this is the only way to resubmit a +multi-line command to the S process in one go.) If the current line does +@cindex multi-line commands, resubmitting +not begin with a prompt, an error is signalled. This feature, coupled +with the command-based motion commands described above, could be used as +a primitive history mechanism. S-mode provides a more sophisticated +mechanism, however, which is described below. + +@node Command History, Hot keys, Process buffer motion, Entering commands +@comment node-name, next, previous, up +@section Command History +@cindex command history +@cindex editing commands +@cindex re-executing commands + +S-mode provides easy-to-use facilities for re-executing or editing +previous commands. An input history of the last few commands is +maintained (by default the last 50 commands are stored, although this +can be changed by setting the variable @code{input-ring-size} in +@vindex input-ring-size +@code{inferior-S-mode-hook}.) The simplest history commands simply +select the next and previous commands in the input history: + +@itemize @bullet +@item +@kbd{M-p} (@code{comint-next-input}) @* +@pindex comint-next-input +Select the previous command in the input history. + +@item +@kbd{M-n} (@code{comint-previous-input}) @* +@pindex comint-next-input +Select the next command in the input history. +@end itemize + +@noindent +For example, pressing @kbd{M-p} once will re-enter the last +command into the process buffer after the prompt but does not send it to +the S process, thus allowing editing or correction of the command before +the S process sees it. Once corrections have been made, press @kbd{RET} +to send the edited command to the S process. + +If you have an idea which command you want from the history, the commands + +@itemize @bullet +@item +@kbd{M-s} (@code{comint-previous-similar-input}) @* +@pindex comint-previous-similar-input +Select the previous command in the history which matches the string +typed so far. + +@item +@kbd{M-S} (@code{comint-next-similar-input}) @* +@pindex comint-next-similar-input +Select the next command in the history which matches the string +typed so far. +@end itemize + +@noindent +may be more useful, as they only select commands starting with those +characters already entered. For instance, if you wanted to re-execute +the last @code{attach()} command, all you need to do is type +@kbd{attach} and then @kbd{M-s} and @kbd{RET}. + +Sometimes you want to re-execute a command that matches a particular +string (a variable name for example) which does not appear at the start +of the command. In this case + +@itemize @bullet +@item +@kbd{M-r} (@code{comint-isearch}) @* +@pindex comint-isearch +Interactively search backwards through the input history for a string. +@end itemize + +@noindent +may be useful. This command is very similar to @code{isearch-backward}, +except that it operates on the input history instead of the buffer text. +After typing @kbd{M-r}, commands which match the search string are +displayed as you enter the string itself. If you entered some text +before pressing @kbd{M-r} then only commands which begin with that text are +considered as candidates, and the string is matched against the +remaining part of the command. Use @kbd{C-r} to search further backwards +and @kbd{C-s} to search forwards. @kbd{RET} sends the selected comand +directly to the S process; use @kbd{ESC} if you wish to edit it first. + +@node Hot keys, Other inferior-S-mode commands, Command History, Entering commands +@comment node-name, next, previous, up +@section Hot keys for common commands + +S-mode provides a number of commands for executing the commonly used +functions. These commands below are basically information-gaining +commands (such as @code{objects()} or @code{search()}) which tend to +clutter up your transcript and for this reason some of the hot keys +display their output in a temporary buffer +instead of the process buffer by default. This behaviour is controlled +by the variable @code{S-execute-in-process-buffer} which, if +@vindex S-execute-in-process-buffer +non-@code{nil}, means that these commands will produce their output in +the process buffer instead. In any case, passing a prefix argument to +the commands (with @kbd{C-u}) will reverse the meaning of +@code{S-execute-in-process-buffer} for that command, i.e. the output +will be displayed in the process buffer if it usually goes to a +temporary buffer, and vice-versa. These are the hot keys that behave in +this way: + +@itemize @bullet +@item +@kbd{C-c C-x} (@code{S-execute-objects}) @* +@pindex S-execute-objects +Sends the @code{objects()} +@pindex objects() +command to the S process. A prefix argument specifies the position on +the search list (use a negative argument to toggle +@code{S-execute-in-process-buffer} as well.) +A quick way to see what objects are in your working +directory. +@cindex objects +@pindex objects() + +@item +@kbd{C-c C-s} (@code{S-execute-search}) @* +@pindex S-execute-search +Sends the @code{search()} +@pindex search() +command to the S process. +@cindex search list +@pindex search() + +@item +@kbd{C-c C-e} (@code{S-execute}) @* +@pindex S-execute +Prompt for an S expression, and evaluate it. +@end itemize + +@code{S-execute} may seem pointless when you could just type the command +in anyway, but it proves useful for `spot' calculations which would +otherwise clutter your transcript, or for evaluating an expression while +partway through entering a command. You can also use this command to +generate new hot keys using the Emacs keyboard macro facilities; +@pxref{Keyboard Macros, Keyboard Macros, Keyboard Macros, emacs, The GNU +Emacs Reference Manual}. +@cindex hot keys +@cindex keyboard short cuts + +The following hot keys do not use @code{S-execute-in-process-buffer} to +decide where to display the output --- they either always display in +the process buffer or in a separate buffer, as indicated: + +@itemize @bullet +@item +@kbd{C-c C-a} (@code{S-execute-attach}) @* +@pindex S-execute-attach +Prompts for a directory to attach to the S process with the +@code{attach()} command. +@pindex attach() +If a numeric prefix argument is given it is used as the position on the +search list to attach the directory; otherwise the S default of 2 is +used. The @code{attach()} command actually executed appears in the +process buffer. + +@item +@kbd{C-c C-l} (@code{S-load-file}) @* +@pindex S-load-file +Prompts for a file to load into the S process using @code{source()}. If +there is an error during loading, you can jump to the error in the file +with @kbd{C-x `} (@code{S-parse-errors}). +@pindex S-parse-errors +@xref{Error Checking} for more details. + +@item +@kbd{C-c C-h} (@code{S-display-help-on-object}) @* +Pops up a help buffer for an S object or function. See @xref{Help} for +more details. + +@item +@kbd{C-c C-q} (@code{S-quit}) @* +@cindex quitting from S-mode +@cindex killing the S process +Sends the @code{q()} +@pindex q() +command to the S process, and cleans up any temporary buffers (such as +help buffers or edit buffers) you may have created along the way. Use +this command when you have finished your S session instead of simply +typing @code{q()} yourself, otherwise you will need to issue the command +@kbd{M-x S-cleanup} +@pindex S-cleanup +@cindex cleaning up +@cindex temporary buffers, killing +@cindex killing temporary buffers +command explicitly to make sure that all the files that need to be saved +have been saved, and that all the temporary buffers have been killed. +@end itemize + +@node Other inferior-S-mode commands, , Hot keys, Entering commands +@comment node-name, next, previous, up +@section Other commands provided by inferior-S-mode + +The following commands are also provided in the process buffer: + +@itemize @bullet +@item +@kbd{C-c C-c} (@code{comint-interrupt-subjob}) @* +@pindex comint-interrupt-subjob +Sends a Control-C signal to the S process. This has the effect of +@cindex aborting S commands +@cindex interrupting S commands +aborting the current command. + +@item +@kbd{C-c C-z} (@code{S-abort}) @* +@pindex S-abort +@pindex comint-stop-subjob +Sends a STOP signal to the S process, killing it immediately. It's not a +good idea to use this, in general: Neither @code{q()} nor @code{.Last} +will be executed and device drivers will not finish cleanly. This +command is provided as a safety to @code{comint-stop-subjob}, which is +usually bound to @kbd{C-c C-z}. If you want to quit from S, use @kbd{C-c +C-q} (@code{S-quit}) instead. +@pindex S-quit +@cindex aborting the S process + +@item +@kbd{C-c C-d} (@code{S-dump-object-into-edit-buffer}) @* +@pindex S-dump-object-into-edit-buffer +Prompts for an object to be edited in an edit buffer. @xref{Editing}. + +@item +@kbd{C-c C-t} (@code{S-tek-mode-toggle}) @* +Toggles Tek graphics mode. @xref{tek4014} for more details. +@end itemize + +@node Editing, Help, Entering commands, Top +@comment node-name, next, previous, up +@chapter Editing S functions + +@cindex editing functions +S-mode provides facilities for editing S objects within your Emacs +session. Most editing is performed on S functions, although in theory +you may edit datasets as well. Edit buffers are always associated with +files, although you may choose to make these files temporary if you +wish. Alternatively, you may make use of a simple yet powerful mechanism +for maintaining backups of text representations of S functions. +Error-checking is performed when S code is loaded into the S process. + +@menu +* Edit buffer:: The edit buffer +* Modification:: Modification flags in edit buffers +* Source Files:: Maintaining S source files +* Source Directories:: Names and locations of dump files +* Error Checking:: Detecting and correcting errors +@end menu + +@node Edit buffer, Modification, , Editing +@comment node-name, next, previous, up +@section The edit buffer +@cindex edit buffer + +To edit an S object, type + +@itemize @bullet +@item +@kbd{C-c C-d} (@code{S-dump-object-into-edit-buffer}) @* +@pindex S-dump-object-into-edit-buffer +Edit an S object in its own edit buffer. +@end itemize + +from within the S process buffer (@code{*S*}). +You will them be prompted for an object to edit: you may either type in +the name of an existing object (for which completion is available using +the @kbd{TAB} key,) +@cindex completion, when prompted for object names +or you may enter the name of a new object. +@cindex creating new objects +@cindex new objects, creating +A buffer will be created containing the text representation of the +requested object or, if you entered the name of a non-existent object at +the prompt and the variable @code{S-insert-function-templates} +@vindex S-insert-function-templates +is non-@code{nil}, you will be presented with a template defined by +@code{S-function-template} +@vindex S-function-template +which defaults to a skeleton function construct. + +You may then edit the function as required. +The edit buffer generated by @code{S-dump-object-into-edit-buffer} is placed +in the @code{S-mode} major mode which provides a number of commands to +facilitate editing S source code. Commands are provided to intelligently +indent S code, evaluate portions of S code and to move around S code +constructs. + +@cindex dump files +@cindex reverting function definitions +@strong{Note:} when you dump a file with @kbd{C-c C-d}, S-mode first +checks to see whether there already exists an edit buffer containing +that object and, if so, pops you directly to that buffer. If not, S-mode +next checks whether there is a file in the appropriate place with the +appropriate name (@xref{Source Files}) and if so, reads in that file. +You can use this facility to return to an object you were editing in a +previous session (and which possibly was never loaded to the S session.) +Finally, if both these tests fail, the S process is consulted and a +@code{dump()} command issued. +@pindex dump() +If you want to force S-mode to ask the S process for the object's +definition (say, to reformat an unmodified buffer or to revert back to +S's idea of the object's definition) pass a prefix argument to +@code{S-dump-object-into-edit-buffer} by typing @kbd{C-u C-c C-d}. + +@menu +* Evaluating code:: Sending code to the S process +* Indenting:: Indenting and formatting S code +* Other edit buffer commands:: Commands for motion, completion and more +@end menu + +@node Evaluating code, Indenting, , Edit buffer +@comment node-name, next, previous, up +@subsection Sending code to the S process + +There are a wide range of commands for sending code to the S process. +The primary command is for loading the current buffer (which usually +contains a single function) into the S process: + +@itemize @bullet +@item +@kbd{C-c C-l} (@code{S-load-file}) @* +@pindex S-load-file +Loads a file into the S process using @code{source()}. +@pindex source() +@end itemize + +@noindent +After typing @kbd{C-c C-l} you will prompted for the name of the file to +load into S; usually this is the current buffer's file which is the +default value (selected by simply pressing @kbd{RET} at the prompt.) +Your will be asked to save the buffer first if it has been modified +(this happens automatically if the buffer was generated with @kbd{C-c +C-d}.) If the buffer is not modified, S-mode assumed its contents are +equivalent to S's value of the function and you will need to confirm +that you want to load the file into S. The file will then be loaded and +you will be returned to the S process. If any errors occur, S-mode will +inform you of this fact: @xref{Error Checking}. + +Other commands are also available for evaluating portions of code in the +S process. You may choose whether both the commands and their output +appear in the process buffer (as if you had typed in the commands +yourself) or if the output alone is echoed. The behaviour is controlled +by the variable @code{S-eval-visibly-p} whose default is @code{nil} +@vindex S-eval-visibly-p +(display output only.) Passing a prefix argument (@kbd{C-u}) to any of +the following commands, however, reverses the meaning of +@code{S-eval-visibly-p} for that command only --- for example @kbd{C-u +C-c C-j} echoes the current line of S-code in the S process buffer, +followed by its output. This method of evaluation is an alternative to +S's @code{source()} function +@pindex source() +@cindex echoing commands when evaluating +@cindex evaluating code with echoed commands +when you want the input as well as the output to be displayed. (You can +sort of do this with @code{source()} when the option @code{echo=T} is +set, except that prompts do not get displayed. S-mode puts prompts in +the right places.) The commands for evaluating code are: + +@itemize @bullet +@cindex evaluating S expressions +@item +@kbd{C-c C-j} (@code{S-eval-line}) @* +@pindex S-eval-line +Send the line containing point to the S process. + +@item +@kbd{C-c M-j} (@code{S-eval-line-and-go}) @* +@pindex S-eval-line-and-go +As above, but returns you to the S process buffer as well. + +@item +@kbd{C-c C-f} or @kbd{ESC C-x} (@code{S-eval-function}) @* +@pindex S-eval-function +Send the S function containing point to the S process. + +@item +@kbd{C-c M-f} (@code{S-eval-function-and-go}) @* +@pindex S-eval-function-and-go +As above, but returns you to the S process buffer as well. + +@item +@kbd{C-c C-r} (@code{S-eval-region}) @* +@pindex S-eval-region +Send the text between point and mark to the S process. + +@item +@kbd{C-c M-r} (@code{S-eval-region-and-go}) @* +@pindex S-eval-region-and-go +As above, but returns you to the S process buffer as well. + +@item +@kbd{C-c C-b} (@code{S-eval-buffer}) @* +@pindex S-eval-buffer +Send the contents of the edit buffer to the S process. + +@item +@kbd{C-c M-b} (@code{S-eval-buffer-and-go}) @* +@pindex S-eval-function-and-go +As above, but returns you to the S process buffer as well. + +@item +@kbd{C-c C-n} (@code{S-eval-line-and-next-line}) @* +@pindex S-eval-line-and-next-line +@cindex stepping through code +@cindex debugging S functins +Sends the current line to the S process, echoing it in the process +buffer, and moves point to the next line. Useful when debugging for +stepping through your code. +@end itemize + +It should be stressed once again that these @code{S-eval-} commands +should only be used for evaluating small portions of code for debugging +purposes, or for generating transcripts from source files. When editing +S functions, @kbd{C-c C-l} is the command to use to update the +function's value. In particular, @code{S-eval-buffer} is now largely +obsolete. + +One final command is provided for spot-evaluations of S code: + +@itemize @bullet +@kbd{C-c C-e} (@code{S-execute-in-tb}) @* +@pindex S-execute-in-tb +Prompt for an S expression and evaluate it. Displays result in a +temporary buffer. +@end itemize + +@noindent +This is useful for quick calculations, etc. + +@node Indenting, Other edit buffer commands, Evaluating code, Edit buffer +@comment node-name, next, previous, up +@subsection Indenting and formatting S code + +S-mode now provides a sophisticated mechanism for indenting S source +code (thanks to Ken'ichi Shibayama.) Compound statements (delimited by +@samp{@{} and @samp{@}}) are indented relative to their enclosing block. +In addition, the braces have been electrified to automatically indent to +the correct position when inserted, and optionally insert a newline at +the appropriate place as well. Lines which continue an incomplete +expression are indented relative to the first line of the expression. +Function definitions, @code{if} statements, calls to @code{expression()} +and loop constructs are all recognised and indented appropriately. User +variables are provided to control the amount if indentation in each +case, and there are also a number of predefined indentation styles to +choose from. @xref{Indentation variables}. + +@cindex comments in S +Comments are also handled specially by S-mode, using an idea borrowed +from the Emacs-Lisp indentation style. Comments beginning with @samp{###} +are aligned to the beginning of the line. Comments beginning with +@samp{##} are aligned to the current level of indentation for the block +containing the comment. Finally, comments beginning with @samp{#} are +aligned to a column on the right (the 40th column by default, but this +value is controlled by the variable @code{comment-column},) +@vindex comment-column +or just after the expression on the line containing the comment if it +extends beyond the indentation column. + +The indentation commands provided by S-mode are: +@cindex indenting +@cindex formatting source code + +@itemize @bullet +@item +@kbd{TAB} (@code{S-indent-command}) @* +Indents the current line as S code. If a prefix argument is given, all +following lines which are part of the same (compound) expression are +indented by the same amount (but relative indents are preserved). + +@item +@kbd{ESC C-q} (@code{S-indent-exp}) @* +Indents each line in the S (compound) expression which follows point. +Very useful for beautifying your S code. + +@item +@kbd{@{} and @kbd{@}} (@code{S-electric-brace}) @* +The braces automatically indent to the correct position when typed. + +@item +@kbd{M-;} (@code{indent-for-comment}) @* +Indents a comment line appropriately, or inserts an empty +(single-@samp{#}) comment. + +@item +@kbd{M-x S-set-style} @* +Set the formatting style in this buffer to be one of the predefined +styles (@code{GNU}, @code{BSD}, @code{K&R} and @code{C++} by default). +This command causes all of the formatting variables to be buffer-local. +@end itemize + +@node Other edit buffer commands, , Indenting, Edit buffer +@comment node-name, next, previous, up +@subsection Commands for motion, completion and more + +A number of commands are provided to move across function definitions +in the edit buffer: +@itemize @bullet +@item +@kbd{ESC C-e} (@code{S-beginning-of-function}) @* +@pindex S-beginning-of-function +Moves point to the beginning of the function containing point. + +@item +@kbd{ESC C-a} (@code{S-end-of-function}) @* +@pindex S-end-of-function +Moves point to the end of the function containing point. + +@item +@kbd{ESC C-h} (@code{S-mark-function}) @* +Places point at the beginning of the S function containing point, and +mark at the end. +@end itemize +@noindent +Don't forget the usual Emacs commands for moving over balanced +expressions and parentheses: @xref{Lists, Lists and Sexps, Lists and +Sexps, Emacs, The GNU Emacs Reference Manual}. + +Completion is also available in the edit buffer: +@itemize @bullet +@item +@kbd{ESC TAB} (@code{S-complete-object-name}) @* +Completes the S object name before point. +@end itemize +Note however that completion is only provided over globally known S +objects (such as system functions) --- it will @emph{not} work for +arguments to functions or other variables local to the function you are +editing. + +Finally, two commands are provided for returning to the S process buffer: + +@itemize @bullet +@item +@kbd{C-c C-z} (@code{S-switch-to-end-of-S}) @* +@pindex S-switch-to-end-of-S +Returns you to the S process buffer, placing point at the end of the +buffer. + +@item +@kbd{C-c C-y} (@code{S-switch-to-S}) @* +@pindex S-switch-to-S +Also returns to to the S process buffer, but leaves point where it is. +@end itemize + +In addition some commands available in the process buffer are also +available in the edit buffer. You can still read help files with +@kbd{C-c C-h}, edit another function with @kbd{C-c C-d} and of course +@kbd{C-c C-l} can be used to load a source file into S. @xref{Other +inferior-S-mode commands} for more details on these commands. + +@node Modification, Source Files, Edit buffer, Editing +@comment node-name, next, previous, up +@section Modification flags in edit buffers + +@cindex modification flag +Within S-mode edit buffers, the modification flag has a slightly +different meaning than it usually does. In general, S-mode tries to set +the modification flag whenever the contents of the edit buffer differ +from S's idea of the objects value, and clears the flag whenever the +edit buffer has been successfully loaded into the S process. Thus you +will be warned whenever you attempt to kill a buffer which represents an +edited (i.e. different) version of the existing S object. + +Edit buffers are marked as temporary buffers within S-mode, and will be +killed whenever @code{S-quit} or @code{S-cleanup} are called. +@pindex S-quit +@pindex S-cleanup +If the modification flag is set, however, you will be warned before that +buffer is killed. + +@node Source Files, Source Directories, Modification, Editing +@comment node-name, next, previous, up +@section Maintaining S source files + +Every edit buffer in S-mode is associated with a @dfn{dump file} on +disk. Dump files are created whenever you type @kbd{C-c C-d} +(@code{S-dump-object-into-edit-buffer}), and may either be deleted +after use, or kept as a backup file or as a means of keeping +several versions of an S function. +@cindex dump files + +@defvr {User Option} S-keep-dump-files +If this has a non-@code{nil} value, then dump files are never deleted. +Otherwise dump files are silently deleted after each use, unless an +error occurs. +@end defvr + +When @code{S-keep-dump-files} is @code{nil}, the dump file is deleted +immediately after it is read into the edit buffer. This is so that you +can kill the edit buffer at any time without leaving the dump file +behind. When loading the file back into S with @kbd{C-c C-l}, the dump +file is again written out to disk and loaded into S. If the load is +successful, the file is again deleted. If there is an error in your +function, however, the file is retained so that you may edit the file at +any time to correct the error. + +When @code{S-keep-dump-files} is non-@code{nil}, dump files are never +deleted. Thus you can maintain a complete text record of the functions +you have editied within S-mode. Backup files are always kept, and so by +using the Emacs numbered backup facility --- @pxref{Backup Names, Single +or Numbered Backups, Single or Numbered Backups, emacs, The Gnu Emacs +Reference Manual}, you can keep a historic record of function +definitions. As long as a dump file exists in the appropriate place for +a particular object, editing that object with @kbd{C-c C-d} finds that +file for editing (unless a prefix argument is given) --- the S process +is not consulted. Thus you can keep comments @emph{outside} the function +definition as a means of documentation that does not clutter the S +object itself. Another useful feature is that you may format the code in +any fashion you please without S re-indenting the code every time you +edit it. These features are particularly useful for project-based work. +@cindex comments +@cindex project work in S +@cindex historic backups + +@cindex autosaving +Dump buffers are always autosaved, regardless of the value of +@code{S-keep-dump-files}. + +When an object is dumped to a file, S-mode adds some comment lines to +the end of the file, such as +@cindex comments at end of file +@example +# Local Variables: +# mode:S +# S-temp-buffer-p:t +# End: +@end example +These are included to ensure that whenever you next edit the file, it is +in the correct mode for editing S source, and that the associated buffer +is marked as a temporary buffer. If @code{S-keep-dump-files} is +@code{nil} and you wish to keep the file associated with the edit +buffer, remove the line +@example +# S-temp-buffer-p:t +@end example +@noindent +and @emph{save} the buffer. +@cindex preserving dump files +@cindex dump files, preserving +The buffer will still be marked as temporary, however, and so deleted +when you quit from S. You can change this by using @kbd{M-x +set-variable} to set the value of @code{S-temp-buffer-p} to @code{nil}. +@vindex S-temp-buffer-p +@cindex temporary buffers + +@node Source Directories, Error Checking, Source Files, Editing +@comment node-name, next, previous, up +@section Names and locations of dump files + +@cindex dump file names +Every dump file should be given a unique file name, usually the dumped +object name with some additions. + +@defvr {User Option} S-dump-filename-template +Template for filenames of dumped objects. @code{%s} +is replaced by the object name. +@end defvr + +@noindent +By default, dump file names are the user name, followed by @samp{.} and +the object and ending with @samp{.S}. Thus if user @code{joe} dumps the +object @code{myfun} the dump file will have name @file{joe.myfun.S}. The +username part is included to avoid clashes when dumping into a +publicly-writable directory, such as @file{/tmp}; you may wish to remove +this part if you are dumping into a directory owned by you. + +@cindex dump file directories +You may also specify the directory in which dump files are written: + +@defvr {User Option} S-source-directory +Directory name (ending in a slash) where S dump files are to be written. +@end defvr + +By default, dump files are always written to @file{/tmp}, which is fine +when @code{S-keep-dump-files} is @code{nil}. If you are keeping dump +files, then you will probably want to keep them somewhere in your home +directory, say @file{~/S-source}. This could be achieved by including +the following line in your @file{.emacs} file: +@cindex @file{.emacs} file +@example +(setq S-source-directory (expand-file-name "~/S-source/")) +@end example + +If you would prefer to keep your dump files in separate directories +depending on the value of some variable, S-mode provides a facility for +this also. + +@defvr {User Option} S-source-directory-generator +Variable whose value is a function which, when called with no arguments, +will return a directory name (ending in @samp{/}) into which dump files +will be written. @code{nil} means use the value of +@code{S-source-directory}. +@end defvr + +@noindent +If the directory generated by this function does not exist but can be +created, you will be asked whether you wish to create the directory. If +you do not or the directory cannot be created, the value of +@code{S-source-directory} is used. + +One application of @code{S-source-directory-generator} is to keep dump +files in some subdirectory of the current S directory: +@example +(setq S-source-directory-generator + '(lambda () + (expand-file-name + (concat + (directory-file-name S-directory) + "/Src/")))) +@end example +@noindent +This is useful if you keep your dump files and you often edit objects +with the same name in different directories. Alternatively, if you +often change your S working directory during an S session, you may like +to keep dump files in some subdirectory of the directory pointed to by +the first element of the current search list. This way you can edit +objects of the same name in different directories during the one S +session: +@cindex search list +@cindex working directory +@example +(setq S-source-directory-generator + '(lambda () + (file-name-as-directory + (expand-file-name (concat + (car S-search-list) + "/.Src"))))) +@end example +@vindex S-search-list + +@node Error Checking, , Source Directories, Editing +@comment node-name, next, previous, up +@section Detecting and correcting errors +@cindex errors +@cindex parsing errors + +After loading a file into the S process with @kbd{C-c C-l}, S-mode will +report whether the load was successful. If it was not (i.e. there was +some sort of error in your code) you can return to the file from the S +process buffer with @kbd{C-x `} (@code{S-parse-errors}). +@pindex S-parse-errors +You will be returned to the offending file (loading it into a buffer if +necessary) with point at the line S reported as containing the error. +You may then correct the error, and reload the file. Note that the +corresponding S object will not be changed until the file has been +successfully loaded; it is for this reason that temporary files +containing errors are never deleted. + +Sometimes the error is not caused by a syntax error (loading a +non-existent file for example.) In this case typing @kbd{C-x `} will +simply display a buffer containing S's error message. You can force this +behaviour (and avoid jumping to the file when there @emph{is} a syntax +error) by passing a prefix argument to @code{S-parse-errors} with +@kbd{C-u C-x `}. + +@node Help, Graphics, Editing, Top +@comment node-name, next, previous, up +@chapter Reading help files in S-mode +@cindex help files + +S-mode provides an easy-to-use facility for reading S help files from +within Emacs. From within the S process buffer or any S-mode edit +buffer, typing @kbd{C-c C-h} (@code{S-display-help-on-object}) +@pindex S-display-help-on-object +will prompt you for the name of an object for which you would like +documentation. Completion is +provided over all objects which have help files. + +If the requested object has documentation, you will be popped into a +buffer (named @code{*help(@var{obj-name})*}) containing the help file. +This buffer is placed in a special `S Help' mode which disables the +usual editing commands but which provides a number +of keys for paging through the help file: + +@itemize @bullet +Help commands: + +@item +@kbd{?} (@code{S-describe-help-mode}) @* +@pindex S-describe-help-mode +Pops up a help buffer with a list of the commands available in S help +mode. + +@item +@kbd{h} (@code{S-display-help-on-object}) @* +@pindex S-display-help-on-object +Pop up a help buffer for a different object + +Paging commands: + +@cindex paging commands in help buffers +@item +@kbd{b} or @kbd{DEL} (@code{scroll-down}) @* +Move one page backwards through the help file. + +@item +@kbd{SPC} (@code{scroll-up}) @* +Move one page forwards through the help file. + +@item +@kbd{>} (@code{beginning-of-buffer}) and @kbd{<} (@code{end-of-buffer}) @* +Move to the beginning and end of the help file, respectively. + +Section-based motion commands: + +@item +@kbd{n} (@code{S-skip-to-next-section}) and @kbd{p} +(@code{S-skip-to-previous-section}) @* Move to the next and previous +@pindex S-skip-to-next-section +@pindex S-skip-to-previous-section +section header in the help file, respectively. A section header consists +of a number of capitalised words, followed by a colon. + +In addition, the @kbd{s} key followed by one of the following letters +will jump to a particular section in the help file: +@pindex S-skip-to-help-section +@table @samp +@item a +ARGUMENTS: + +@item b +BACKGROUND: + +@item B +BUGS: + +@item d +DETAILS: + +@item D +DESCRIPTION: + +@item e +EXAMPLES: + +@item n +NOTE: + +@item o +OPTIONAL ARGUMENTS: + +@item r +REQUIRED ARGUMENTS: + +@item R +REFERENCES: + +@item s +SIDE EFFECTS: + +@item s +SEE ALSO: + +@item u +USAGE: + +@item v +VALUE: + +@item < +Jumps to beginning of file + +@item > +Jumps to end of file + +@item ? +Pops up a help buffer with a list of the defined section motion keys. +@end table + +Miscellaneous: + +@item +@kbd{r} (@code{S-eval-region}) @* +@pindex S-eval-region +Send the contents of the current region to the S process. Useful for +running examples in help files. + +@item +@kbd{/} (@code{isearch-forward}) @* +Same as @kbd{C-s}. + +Quit commands: + +@item +@kbd{q} (@code{S-switch-to-end-of-S}) @* +@pindex S-switch-to-end-of-S +Returns to the S process buffer in another window, leaving the help +window visible. + +@item +@kbd{x} (@code{S-kill-buffer-and-go}) @* +Return to the S process, killing this help buffer. +@end itemize + +In addition, all of the S-mode commands available in the edit buffers +are also available in S help mode (@xref{Edit buffer}). Of course, the +usual (non-editing) Emacs commands are available, and for convenience +the digits and @samp{-} act as prefix arguments. + +If a help buffer already exists for an object for which help is +requested, that buffer is popped to immediately; the S process is not +consulted at all. If the contents of the help file have changed, you +either need to kill the help buffer first, or pass a prefix argument +(with @kbd{C-u}) to @code{S-display-help-on-object}. + +Help buffers are marked as temporary buffers in S-mode, and are deleted +when @code{S-quit} or @code{S-cleanup} are called. +@pindex S-quit +@pindex S-cleanup +@cindex temporary buffers + +@node Graphics, Bugs, Help, Top +@comment node-name, next, previous, up +@chapter Using graphics with S-mode + +@cindex graphics +One of the main features of the @code{S} package is its ability to +generate high-resolution graphics plots, and S-mode provides a number of +features for dealing with such plots. + +@menu +* printer:: The printer() graphics driver +* tek4014:: The tek4014() graphics driver +* X11:: The X11() (and other X-windows based) driver +@end menu + +@node printer, tek4014, , Graphics +@comment node-name, next, previous, up +@section Using S-mode with the @code{printer()} driver + +This is the simplest (and least desirable) method of using graphics +within S-mode. S's @code{printer()} device driver produces crude +character based plots which can be contained within the S process buffer +itself. To start using character graphics, issue the S command +@example +printer(width=79) +@end example +@pindex printer() +(the @code{width=79} argument prevents Emacs line-wrapping at column +80 on an 80-column terminal. Use a different value for a terminal with +a different number of columns.) Plotting commands do not generate +graphics immediately, but are stored until the @code{show()} command +is issued, which displays the current figure. + +@node tek4014, X11, printer, Graphics +@comment node-name, next, previous, up +@section Using S-mode with the @code{tek4014()} driver +@cindex tek4014 terminal + +When using @code{S} from the shell with the @code{tek4014()} driver +active, control-codes are sent to your terminal to generate +high-resolution graphics plots. The terminal recognizes these +control-codes as graphics commands and duly generates the appropriate +plots. When running @code{S} from Emacs, however, the control-codes are +not treated specially by Emacs and simply appear as ``garbage'' in your +@code{S} process buffer. + +One way around this is to find out what tty you are using (by using the +Unix @code{tty} command @emph{outside} of Emacs) and using the +@code{file=} argument to the @code{tek4014()} function to divert the +graphics control codes directly to the terminal. For example, if the +@code{tty} command returned @file{/dev/ttyp1} then the S command +@example +tek4014(file="/dev/ttyp1") +@end example +will send graphics to your terminal. You may even use some other +terminal which you are logged on to to have the graphics appear on +another terminal. There are some problems with this method, however: +depending on your terminal you may need to switch into graphics mode +before issuing the plotting command, and if you are displaying the +graphics on the same terminal as your Emacs session, you will need to +switch back to text mode afterwards. Issuing commands while in graphics +mode presents its own problems, because control-codes issued by Emacs +interfere with the display. + +S-mode attempts to automate this procedure by detecting output +from @code{S} commands which looks like Tek graphics control-codes and +sends those control-codes directly to the terminal. This behaviour is +enabled by setting the variable @code{S-tek-mode} to any non-@code{nil} +@vindex S-tek-mode +value (which may be achieved by using the function @code{S-tek-mode-toggle}, +@pindex S-tek-mode-toggle +bound to @kbd{C-c C-t} by default. Tek mode is designed to work with Tek +terminals which use the same screen to share graphics an text and also +with terminals which provide separate screens. In the former case +(tested on a Vis603 terminal) the variable +@code{S-tek-pause-for-graphics} should be set to @code{t}; in the latter +case (tested using @code{xterm}'s Tek emulation facilities) +@code{S-tek-pause-for-graphics} should be set to @code{nil}. +@vindex S-tek-pause-for-graphics + +This mode depends on being able to work out where the graphics finish +and normal (text) output starts. In the easiest case, it finishes with +your prompt and S-mode has no trouble detecting that. Sometimes +plotting functions also display some text afterwards, and provided the +function finishes and your prompt is displayed @emph{at the start} of a +line this is no problem either, but make sure any such function you +write finishes any text with a newline. Functions like +@example +badfun <- function() @{ plot(1:10) cat("Hello") invisible() @} +@end example +@noindent +will break the graphics detector. Other functions, such as +@code{gam(obj,ask=T)} present a menu after the plot and wait for input (and +so your prompt isn't displayed). The variable +@code{S-tek-possible-graph-prompts} +@vindex S-tek-possible-graph-prompts +is a regular expression used to detect any alternative prompt used in +this case. + +When the graphics display has completed, press any key to return to +your Emacs display. This mode also works with the @code{ask=T} option to +@code{tek4014()}, however any single key is now the appropriate response to +the @samp{GO?} prompt. + +Unexpected redisplays of the Emacs screen (such as caused by +@code{display-time} or garbage collection) can possibly send garbage to +your graphics display, but unfortunately there seems to way to prevent +this. + +If you have a very simple prompt, it may by chance appear in the +graphics output which could possibly cause problems; if this occurs +you will be given a warning. It is advisable to choose a prompt with +at least two characters. If your prompt changes during the S session, +be sure to tell the Tek graphics detector with @code{M-x +S-tek-get-simple-prompt}. +@pindex S-tek-get-simple-prompt + +When @code{S-tek-mode} is enabled, S-mode will make your Emacs process +unusable while waiting for the first output from a function (so it can +determine whether or not it's graphics output). You may be stuck for a +long time when executing a time-consuming function that produces no +output. If this becomes a problem, use @kbd{C-c C-t} to turn Tek mode on +just before pressing @kbd{RET} to issue a plotting command, and turn Tek +mode off again after the plotting command has completed. + +@subsection Warning + +Tek mode is really an experimental feature of S-mode, and has only been +tested on one system, and even then not particularly thoroughly. If it +works for you, well and good, but don't be surprised if it takes some +tinkering before it produces any results on your system. @xref{Bugs} for +a few of the things that can go wrong. + +@node X11, , tek4014, Graphics +@comment node-name, next, previous, up +@section Using S-mode with windowing devices + +@cindex X windows +Of course, the ideal way to use graphics with S-mode is to use a +windowing system. Under X windows, this requires that the DISPLAY +environment variable is appropriately set, which may not always be the +case within your Emacs process. S-mode provides a facility for setting +the value of DISPLAY before the S process is started if the variable +@code{S-ask-about-display} +@pindex S-ask-about-display +is non-@code{nil}. @xref{Customization} for details of this variable, +and @pxref{Starting Up} for information on how to set the value of +DISPLAY when beginning an S session. + +@node Bugs, Installation, Graphics, Top +@chapter Known bugs in S-mode +@cindex bugs + +@itemize @bullet +@item +Commands like @code{S-display-help-on-object} and list completion cannot +be used while the user is entering a multi-line command. The only real +fix in this situation is to use another S process. + +@item +The @code{S-eval-} commands can leave point in the S process buffer in +the wrong place when point is at the same position as the last process +output. This proves difficult to fix, in general, as we need to consider +all @emph{windows} with @code{window-point} at the right place. + +@item +It's possible to clear the modification flag (say, by saving the buffer) +with the edit buffer not having been loaded into S. + +@item +Backup files can sometimes be left behind, even when +@code{S-keep-dump-files} is @code{nil}. + +@item +Passing an incomplete S expression to @code{S-execute} causes S-mode to +hang. + +@item +Completing over lists indexed with @samp{$} destroys the value of +@code{.Last.value} +@vindex .Last.value + +@item +The function-based commands don't always work as expected on functions +whose body is not a parenthesised or compound expression, and don't even +recognise anonymous functions (i.e. functions not assigned to any variable). + +@item +Multi-line commands could be handled better by the command history +mechanism. + +@item +There's a zillion things wrong with Tek-mode: + +@itemize - +@item +Any graphics output that does not come directly after the command is not +detected. + +@item +Graphics output that does not end with some text (either the prompt or +something which matches @code{S-tek-possible-graph-prompts}) causes +S-mode to hang. + +@item +Spurious junk gets sent to the graphics display whenever Emacs updates +its display --- @code{display-time} (which updates the mode line) and +garbage collection (which puts a message in the echo area) are the main +culprits. If only there were a way to stop Emacs from redisplaying for a +time @dots{} + +@item +Interaction with the plot (via the crosshair cursor) is not possible. + +@item +@code{S-tek-mode} should really be a minor mode. +@end itemize + +Let's face it, Tek mode is flaky. It really needs a major overhaul by +someone who really knows about Tek control codes. It needs to be written +using sentinels to detect the start and end of graphics streams, and an +efficient method for swapping between text and graphics modes, including +support for terminals with separate graphics and text screens. Anyone +who wants to have a go at it is more than welcome! +@end itemize + +Until the end of August 1992, please send bug reports to +@code{dsmith@@stats.adelaide.edu.au}. After this date, mail to that +address will not be answered for some time; please contact Frank Ritter +(@code{Frank_Ritter@@SHAMO.SOAR.CS.CMU.EDU}) or any of the other authors +then (please @code{CC:} to @code{dsmith@@stats} as well though -- you +never know your luck!) Comments, suggestions, words of praise and large +cash donations are also more than welcome. + +@node Installation, Customization, Bugs, Top +@comment node-name, next, previous, up +@appendix Installing S-mode on your system +@cindex installation + +The following section details those steps necessary to get S-mode +running on your system. + +First of all, you need to create a directory (say, @file{~/elisp}) to +place the Emacs-Lisp files. Copy @file{S.el}, @file{S-tek.el}, +@file{comint.el}, @file{comint-isearch.el} and @file{comint-extra.el} to +that directory, and add the lines +@example +(setq load-path (cons (expand-file-name "~/elisp") load-path)) +(autoload 'S "S" "Run an inferior S process" t) +(autoload 's-mode "S" "Mode for editing S source" t) +@end example +@noindent +to your @file{.emacs} file. +@cindex @file{.emacs} file +@cindex load path +@vindex load-path + +This will be enough to get S-mode running on most systems --- +@pxref{Starting Up} for details on starting S-mode. If it does not work, +@pxref{System dependent} for other variables you may need to +change. @xref{Customization} for other variables you may wish to set in +your @file{.emacs} file, but it is suggested you defer this section +until you are more familiar with S-mode. + +It is recommended that the @code{.el} +files all be byte-compiled +@cindex byte compilation +with @kbd{M-x byte-compile-file} +@pindex byte-compile-file +for efficiency. + +@menu +* System dependent:: Other variables you may need to change +@end menu + +@node System dependent, , , Installation +@comment node-name, next, previous, up +@appendixsec Other variables you may need to change + +If you run the S program (from the shell) with a command other than +@samp{Splus} you will need to set the variable @code{inferior-S-program} +@vindex inferior-S-program +to the name of the appropriate program by including a line such as +@cindex S program name +@cindex name of S program +@cindex command to run S program +@example +(setq inferior-S-program "S+") +@end example +@noindent +in your @file{.emacs} file +@cindex @file{.emacs} file +@noindent +(substituting @samp{S+} for the name of your S program.) + +If you need to call this program with any arguments, the variable you +@cindex arguments to S program +need to set is dependent on the value of @code{inferior-S-program}; for +example if it is @code{"Splus"}, set the variable +@code{inferior-Splus-args} +@vindex inferior-Splus-args +to a string of arguments to the @code{Splus} program. If +@code{inferior-S-program} has some other value, substitute the +@code{Splus} part of @code{inferior-Splus-args} with the appropriate +program name. There aren't many instances where you need to call S with +arguments, however: in particular do not call the S program with the +@samp{-e} command-line editor argument since S-mode provides this +feature for you. + +If you are running an older version of S, you may need to set the +@cindex versions of S +variable @code{S-version-running} +@vindex S-version-running +to reflect this fact. The default is @code{"3.0"} which indicates the +August '91 revision; any other value indicates an older version. +@c For future compatibility reasons, please use the one of the following +@c values when setting this variable: +@c @table @code +@c @item "3.0" +@c Version 3.0 (August '91) of S/Splus (default) +@c +@c @item "2.3" +@c Version 2.3 of S/Splus +@c +@c @item "old" +@c Any older version +@c @end table +@c @noindent +This variable is effective only when S-mode is @emph{loaded}; setting it +during an S session has no effect. + +@cindex Splus +@vindex S-plus +If you are running Splus (the enhanced version of S from Statsci) you +may also need to set the variable @code{S-plus} to @code{t}. If your +value of @code{inferior-S-program} is @code{"S+"} or @code{Splus} this +will not be necessary, however; @code{S-plus} defaults to @code{t} in +this case. + +Finally, if you use a non-standard prompt within S, you will need to set the +variable @code{inferior-S-prompt} +@cindex prompts in S +@vindex inferior-S-prompt +to a regular expression which will match both the primary prompt (@code{"> "} +@cindex primary prompt +by default) and the continuing prompt (default of @code{"+ "}.) The +@cindex continuing prompt +default value of this variable matches S's default prompts. For example, +if you use (@code{"$ "}) as your primary prompt (you have +@w{@code{options(prompt="$ ")}} in your @code{.First} function), add the +@pindex options() +@cindex @code{.First} function +following line to your @file{.emacs}: +@example +(setq inferior-S-prompt "^\\(\\+\\|[^\\$]*\\$\\) *") +@end example +@noindent +You will also need to set the variable @code{inferior-S-primary-prompt} +@vindex inferior-S-primary-prompt +to a regular expression which matches the primary prompt only. Do not +anchor the regexp to the beginning of the line with @samp{^}. Once +again, the default value matches S's default prompt; in the example +above the appropriate value would be @code{"[^\\$]*\\$ *"}. + +Once these variables are set appropriately, S-mode should work on any +system. + +@node Customization, Concept Index, Installation, Top +@comment node-name, next, previous, up +@appendix Customizing S-mode +@cindex customization + +S-mode can be easily customised to your taste simply by including the +appropriate lines in your @file{.emacs} file. There are numerous +variables which affect the behaviour of S-mode in certain situations +which can be modified to your liking. Keybindings may be set or changed +to your preferences, and for per-buffer customizations hooks are also +available. + +@menu +* Variables:: Variables for customization +* Hooks:: Customizing S-mode with hooks +* Keybindings:: Changing the default S-mode keybindings +@end menu + +@node Variables, Hooks, , Customization +@comment node-name, next, previous, up +@appendixsec Variables for customization +@cindex variables + +S-mode is easily customisable by means of setting variables in your +@file{.emacs} file. +@cindex @file{.emacs} file +In most cases, you can change defaults by including lines of the form +@cindex defaults +@example +(setq @var{variable-name} @var{value}) +@end example +@noindent +in your @file{.emacs}. + +In what follows, variable names will be listed along with their +descriptions and default values. Just substitute the variable name and +the new value into the template above. + +@menu +* Variables for starting S:: Variables for starting S +* Dump file variables:: Variables for dump files +* Indentation variables:: Variables controlling indentation +* Variables controlling interaction:: Variables controlling interaction with the S process +@end menu + +@node Variables for starting S, Dump file variables, , Variables +@comment node-name, next, previous, up +@appendixsubsec Variables for starting S + +@defvr {User Option} S-ask-for-S-directory +Default: @code{t} @* +@cindex starting directory +@cindex directories +If this variable has a non-@code{nil} value, then every time S-mode is +run with @kbd{M-x S} +@pindex S +you will be prompted for a directory to use as the working directory for +your S session; this directory should have a @file{.Data} subdirectory. +@cindex @file{.Data} directory +If the value of @code{S-ask-for-S-directory} is @code{nil}, the value of +@code{S-directory} +@vindex S-directory +is used as the working directory. +@end defvr + +@defvr {User Option} S-directory +Default: Your home directory @* +The working directory for your S session if @code{S-ask-for-S-directory} +is @code{nil}, and the default when prompting for a directory if it is +not. For example, you may wish to set this to the value of the current +buffer's working directory before starting S by adding the following +line to your @file{.emacs} file (@xref{Hooks}) +@cindex @file{.emacs} file +@example +(setq S-pre-run-hook + '((lambda () (setq S-directory default-directory)))) +@end example +@end defvr + +@defvr {User Option} S-ask-about-display +Default: @code{nil} @* +If this variable has a non-@code{nil} value, then every time S-mode is +run with @kbd{M-x S} +@pindex S +you will be asked for a value for the @code{DISPLAY} environment +variable +@cindex DISPLAY environment variable +@cindex environment variables +to be used in the current S session. If this variable is not set +correctly, S will not be able to create any windows under the X +windowing environment. +@cindex X windows +Completion is provided over the @code{X-displays-list} variable; the +default is the current value of @code{DISPLAY}. This feature is useful +is you often run S on a different display than that of the machine you +are running S from. If +@code{S-ask-about-display} is @code{nil}, the current value of +@code{DISPLAY} is used. +@end defvr + +@defvr {User Option} X-displays-list +Default: @code{'(":0.0")} @* +List of possible values for the @code{DISPLAY} environment variable, +provided for completion when prompting for such a value. +@end defvr + +@node Dump file variables, Indentation variables, Variables for starting S, Variables +@comment node-name, next, previous, up +@appendixsubsec Variables for dump files + +@defvr {User Option} S-insert-function-templates +Default: @code{t} @* +If this variable has a non-@code{nil} value, then dumping a non-existent +object will result in the edit buffer containing a skeleton function +definition, ready for editing. +@end defvr + +@defvr {User Option} S-source-directory +Default: @code{"/tmp/"} @* +@cindex dump files +Directory name (ending in @samp{/}) in which dump files are placed. This +should always be a writable directory. +@end defvr + +@defvr {User Option} S-source-directory-generator +Default: @code{nil} @* +Alternative, dynamic method of specifying the directory for dump files. +@end defvr + +@defvr {User Option} S-dump-filename-template +Default: @var{user_name}@code{.}@var{object_name}@code{.S} @* +Naming system to use for dumped object files. @xref{Source Directories} +for details of this and the previous two variables. +@end defvr + +@defvr {User Option} S-keep-dump-files +Default: @code{nil} @* +Boolean flag signifying whether to keep dump files or to delete them +after each use. @xref{Source Files} for more details. +@end defvr + +@node Indentation variables, Variables controlling interaction, Dump file variables, Variables +@comment node-name, next, previous, up +@appendixsubsec Variables controlling indentation +@cindex formatting source code +@cindex indentation + +@defvr {User Option} S-tab-always-indent +Default: @code{t} @* +If non-@code{nil}, then @kbd{TAB} in the edit buffer always indents the +current line, regardless of the position of point in the line. +Otherwise, indentation is only performed if point is in the lines +indentation, and a tab character is inserted is point is after the first +nonblank character. +@end defvr + +@defvr {User Option} S-auto-newline +Default: @code{nil} @* +Non-@code{nil} means automatically newline before and after braces +inserted in S code. +@end defvr + +@defvr {User Option} S-indent-level +Default: 2 @* +Extra indentation of S statement sub-block with respect to enclosing +braces. +@end defvr + +@defvr {User Option} S-brace-imaginary-offset +Default: 0 @* +Extra indentation (over sub-block indentation) for block following an +open brace which follows on the same line as a statement. +@end defvr + +@defvr {User Option} S-brace-offset +Default: 0 @* +Extra indentation for braces, compared with other text in same context. +@end defvr + +@defvr {User Option} S-continued-statement-offset +Default: 0 @* +Extra indent for lines not starting new statements. +@end defvr + +@defvr {User Option} S-continued-brace-offset +Default: 0 @* +Extra indent for substatements that start with open-braces. +This is in addition to @code{S-continued-statement-offset}. +@end defvr + +@defvr {User Option} S-arg-function-offset +Default: 2 @* +Extra indent for arguments of function @code{foo} when it is called as +the value of an argument to another function in +@code{arg=foo(...)} form. If not number, the statements are indented at +open-parenthesis following @code{foo}. +@end defvr + +@defvr {User Option} S-expression-offset +Default: 4 @* +Extra indent for internal substatements of the call to +@code{expression()} specified in +@pindex expression() +@example +@code{obj <- expression(...)} +@end example +@noindent +form. If not a number, the statements are indented at open-parenthesis +following @samp{expression}. +@end defvr + +@noindent +In addition, a number of default styles are defined for you (in +@code{S-style-alist}): +@vindex S-style-alist + +@defvr {User Option} S-default-style +Default: @code{GNU} @* +The default formatting style to use in edit buffers: @xref{Edit buffer} +for more details. +@end defvr + +@node Variables controlling interaction, , Indentation variables, Variables +@comment node-name, next, previous, up +@appendixsubsec Variables controlling interaction with the S process + +@defvr {User Option} input-ring-size +Default: 50 @* +Number of commands to store in the command history. +@end defvr + +@defvr {User Option} S-execute-in-process-buffer +Default: @code{nil} @* +If this is @code{nil}, then the @code{S-execute-} commands (@pxref{Other +inferior-S-mode commands}) output to a temporary buffer. Otherwise, the +output goes to the S process. +@end defvr + +@defvr {User Option} S-eval-visibly-p +Default: @code{nil} @* +If non-@code{nil}, then the @code{S-eval-} commands (@pxref{Edit +buffer}) echo the S commands in the process buffer by default. In any +case, passing a prefix argument to the eval command reverses the meaning +of this variable. +@end defvr + +@node Hooks, Keybindings, Variables, Customization +@comment node-name, next, previous, up +@appendixsec Customizing S-mode with hooks +@cindex hooks + +S-mode provides five hooks, as follows: + +@defvr {Hook} S-mode-hook +Called every time @code{S-mode} is run, i.e. every time an edit buffer +is generated. +@end defvr + +@defvr {Hook} S-pre-run-hook +Called before the S process is started with @kbd{M-x S}. +@pindex S +@end defvr + +@defvr {Hook} S-mode-load-hook +Called just after the file @file{S.el} is loaded. Useful for setting up +your keybindings, etc. +@end defvr + +@defvr {Hook} inferior-S-mode-hook +Called just after the S process starts up, when the S process buffer is +initialised. +@end defvr + +@defvr {Hook} S-help-mode-hook +Called every time an S help buffer is generated. +@end defvr + +@node Keybindings, , Hooks, Customization +@appendixsec Changing the default S-mode keybindings + +S-mode provides a separate keymap variable for the S process buffer, for +edit buffers and for help buffers. + +@defvr {Keymap} inferior-S-mode-map +@vindex comint-mode-map +Keymap used in the S process buffer. The bindings from +@code{comint-mode-map} are automatically inherited. +@end defvr + +@defvr {Keymap} S-mode-map +Keymap used within edit buffers. +@end defvr + +@defvr {Keymap} S-help-mode-map +Keymap used within help buffers. In addition, @code{S-help-sec-map} is +the keymap for the @samp{s} prefix key. Keys defined in +@code{S-help-sec-keys-alist} are automatically inserted into this keymap +@vindex S-help-sec-keys-alist +when S-mode is loaded. +@end defvr + +@node Concept Index, Variable and command index, Customization, Top +@comment node-name, next, previous, up +@unnumbered Concept Index + +@printindex cp + +@node Variable and command index, , Concept Index, Top +@unnumbered Variable and command index + +@printindex vr + +@contents + +@bye diff --git a/S-tek.el b/S-tek.el new file mode 100644 index 000000000..2e145bd85 --- /dev/null +++ b/S-tek.el @@ -0,0 +1,217 @@ +;;;; -*- Mode: Emacs-Lisp -*- +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; File : S-tek.el +;;;; Authors : David Smith +;;;; Created On : June 25, 1992 +;;;; Last Modified By: David Smith +;;;; Last Modified On: Mon Jun 29 14:51:26 CST 1992 +;;;; Version : 1.1 +;;;; +;;;; PURPOSE +;;;; Tek graphics support of S-mode. +;;;; Component of the S-mode distribution -- see file S.el for info +;;;; +;;;; Copyright 1992 David Smith dsmith@stats.adelaide.edu.au +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; Tek support for S-mode 25-Jun-92 +;;; Copyright 1992 David Smith (dsmith@stats.adelaide.edu.au) +;;; +;;; This file forms part of the S-mode package defined in the file S.el +;;; and is autoloaded as required. + +;;; GENERAL DISCLAIMER +;;; +;;; This program is free software; you can redistribute it +;;; and/or modify it under the terms of the GNU General Public +;;; License as published by the Free Software Foundation; either +;;; version 1, or (at your option) any later version. +;;; +;;; This program is distributed in the hope that it will be +;;; useful, but WITHOUT ANY WARRANTY; without even the implied +;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +;;; PURPOSE. See the GNU General Public License for more +;;; details. +;;; +;;; You should have received a copy of the GNU General Public +;;; License along with this program; if not, write to the Free +;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +;;; 02139, USA. +;;; +;;; In short: you may use this code any way you like, as long as you +;;; don't charge money for it, remove this notice, or hold anyone liable +;;; for its results. + +;;; +;;; OVERVIEW +;;; + +;;; If S-tek-mode is non-nil, then whenever output from an S +;;; command looks like Tek graphics (i.e. starts with S-tek-graphics-re, +;;; such as sent by the tek4014() driver) it is sent straight to the +;;; terminal. This mode may be toggled with \\[S-tek-mode-toggle]. +;;; +;;; This mode depends on being able to work out where the graphics finish +;;; and normal (text) output starts. In the easiest case, it finishes with +;;; your prompt and S-mode has no trouble detecting that. Sometimes +;;; plotting functions also display some text afterwards, and provided the +;;; function finishes and your prompt is displayed *at the start* of a +;;; line this is no problem either, but make sure any such function you +;;; write finishes any text with a newline. Functions like +;;; +;;; badfun <- function() { plot(1:10) cat(\"Hello\") invisible() } +;;; +;;; will break the graphics detector. Other functions, such as +;;; `gam(obj,ask=T)' present a menu after the plot and wait for input (and +;;; so your prompt isn't displayed). The variable S-tek-possible-graph-prompts +;;; is a regular expression used to detect any alternative prompt used in +;;; this case. +;;; +;;; When the graphics display has completed, press any key to return to +;;; your Emacs display. This mode also works with the \"ask=T\" option to +;;; tek4014(), however any single key is now the appropriate response to +;;; the \"GO?\" prompt. +;;; +;;; Unexpected redisplays of the Emacs screen (such as caused by +;;; display-time or garbage collection) can possibly send garbage to your +;;; graphics display, but unfortunately there seems to way to prevent this. +;;; +;;; If you have a very simple prompt, it may by chance appear in the +;;; graphics output which could possibly cause problems; if this occurs +;;; you will be given a warning. It is advisable to choose a prompt with +;;; at least two characters. Tek mode relies on your prompt not changing +;;; while it is active. If your prompt changes while S-tek-mode is t be +;;; sure to tell the Tek graphics detector with +;;; \\[S-tek-get-simple-prompt], or toggle S-tek-mode twice with +;;; \\[S-tek-mode-toggle]. +;;; +;;; When this mode is enabled, S-mode will make your Emacs process +;;; unusable while waiting for the first output from a function (so it can +;;; determine whether or not it's graphics). You may be stuck for a long +;;; time when executing a time-consuming function that produces no output. + +;;; +;;; Internal variables to S tek mode. +;;; + +(defvar S-tek-graphics-re "\035\\|\033" + "Regexp signalling start of Tek graphics.") + +(defvar S-tek-go-string "\ v @\GO? " + "String which, if found in TEK graphics, signals that S is waiting +for another plot.") + +(defvar S-tek-graphics-end-string "\C-_\033:" + "Regexp which, along with S-tek-simple-prompt, signals end of graphics.") + +(defvar S-tek-prompt-warning-given t + "Flag, t if warning given about prompt found in graphics.") + +(defvar S-tek-enter-tek-mode-code "\e[?38h" + "Control codes to enter Tek mode.") + +(defvar S-tek-leave-tek-mode-code + (if (string= (getenv "TERM") "xterm") + "\e\C-c" "\C-x") + "Control codes to leave Tek mode.") + +;;; +;;; The Tek driver itself +;;; + +(defun S-tek-looking-at-string (str) + "Like looking-at, but does a simple string search" + (string= str (buffer-substring (point) (+ (point) (length str))))) + +(defun S-tek-looking-after-string (str) + "Like S-tek-looking-at-string, but considers text before point" + (string= str (buffer-substring (point) (- (point) (length str))))) + +(defun S-tek-wait-for-eog () +;;; Waits until the Tek graphics output is completed, and puts point just +;;; before the prompt + (let* ((cbuffer (current-buffer)) + (sprocess (get-process "S")) + (sbuffer (process-buffer sprocess)) + (limit comint-last-input-end) + result) + (set-buffer sbuffer) + (if (not S-tek-simple-prompt) + (S-tek-get-simple-prompt)) + (while (progn + (accept-process-output sprocess) + (goto-char (point-max)) + (not (if (or (search-backward S-tek-simple-prompt limit t) + (re-search-backward S-tek-possible-graph-prompts limit t)) + (cond + ((S-tek-looking-after-string S-tek-graphics-end-string) + (setq result 'done)) + ((S-tek-looking-after-string "\n") + ;; Found prompt at bol. This means that graphics finished + ;; some time ago; go back and get it + (search-backward S-tek-graphics-end-string nil t) + (search-forward S-tek-graphics-end-string nil t) + ; put point at eog + (setq result 'done)) + (t + ;; Other alternatives mean prompt found in middle of graphics + (if S-tek-prompt-warning-given nil + (message "Prompt found in Tek graphics. Maybe you should change it.") + (sleep-for 5) + (setq S-tek-prompt-warning-given t)) + nil)) + ;; Prompt not found, maybe a GO? is waiting? + (if (not (search-backward S-tek-go-string limit t)) nil + ;; GO? found + (search-forward S-tek-go-string) ; Put point at end of GO + (setq result 'go)))))) + (set-buffer cbuffer) + result)) + +;;; (fset 'real-sit-for (symbol-function 'sit-for)) +;;; (fset 'real-set-buffer-modofied-p (symbol-function 'set-buffer-modified-p)) + +(defun S-tek-snarf-graphics nil + ;; We're in the S process buffer, and the output + ;; from an S command is about to appear. If it's Tek graphics, + ;; pull it out and plot it, else leave it alone. + (accept-process-output (get-process "S")) + (let* ((pmark (process-mark (get-buffer-process (current-buffer)))) + (output (buffer-substring comint-last-input-end pmark)) + graphics + graph-type) + (if (string-match S-tek-graphics-re output) + (progn + (garbage-collect) + ;; Hopefully sentinels will not cause another collection, causing + ;; a message which looks ugly in the graphics. + (message + (concat "Grabbing graphics ... wait " + (if S-tek-pause-for-graphics + "[Press any key to clear graphics]"))) + (setq graph-type (S-tek-wait-for-eog)) + (save-excursion + (setq graphics + (buffer-substring comint-last-input-end + (point))) + (delete-region comint-last-input-end (point)) + (send-string-to-terminal S-tek-enter-tek-mode-code) + (send-string-to-terminal graphics) + (send-string-to-terminal S-tek-leave-tek-mode-code) + (if (eq graph-type 'go) + (progn + (if S-tek-pause-for-graphics nil + (message "Press a key for next plot.")) + (read-char) + (inferior-S-send-input) + (forward-line -1) + (delete-blank-lines)) + (if S-tek-pause-for-graphics + (read-char)) + (redraw-display))) + (goto-char (point-max)))))) + +;;; Provide package + +(provide 'S-tek) \ No newline at end of file diff --git a/S.el b/S.el index e6e7b132a..a576cb5b5 100644 --- a/S.el +++ b/S.el @@ -1,13 +1,51 @@ -;;; S mode for GNU Emacs 6-Nov-91 -;;; Copyright 1989,1991 Doug Bates bates@stat.wisc.edu -;;; Ed Kademan kademan@stat.wisc.edu -;;; Frank Ritter ritter@psy.cmu.edu -;;; (or @cs.cmu.edu) +;;;; -*- Mode: Emacs-Lisp -*- +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; File : S.el +;;;; Authors : Doug Bates +;;;; : Ed Kademan +;;;; : Frank Ritter +;;;; : David Smith +;;;; Created On : October 14, 1991 +;;;; Last Modified By: David Smith +;;;; Last Modified On: Mon Jun 29 15:04:26 CST 1992 +;;;; Version : 3.41 +;;;; +;;;; Lisp-dir-entry : S-mode| +;;;; Doug Bates, Ed Kademan, Frank Ritter, David Smith| +;;;; dsmith@stats.adelaide.edu.au| +;;;; Interface to the S/Splus statistical software packages| +;;;; 92-06-29| +;;;; 3.4| +;;;; /attunga.stats.adelaide.edu.au:pub/S-mode/S-mode3.4.tar.Z +;;;; +;;;; PURPOSE +;;;; Interface to the S/Splus statistical software packages +;;;; +;;;; Copyright 1989,1991,1992 Doug Bates bates@stat.wisc.edu +;;;; Ed Kademan kademan@stat.wisc.edu +;;;; Frank Ritter ritter@psy.cmu.edu +;;;; (or @cs.cmu.edu) +;;;; David Smith dsmith@stats.adelaide.edu.au +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; The Changelog is at the end of this file. + +;;; CREDITS. +;;; Thanks to shiba@shun.isac.co.jp (Ken'ichi "Modal" Shibayama) for +;;; the indenting code. +;;; Thanks also to maechler@stat.math.ethz.ch (Martin Maechler) for +;;; suggestions and bug fixes. +;;; S-eval-line-and-next-line is based on a function by Rod Ball +;;; (rod@marcam.dsir.govt.nz) +;;; +;;; Also thanks from David Smith to the previous authors for all their +;;; help and suggestions. + ;;; BRIEF OVERVIEW ;;; Supports stuctured editing of S (a statistics package) ;;; functions that is integrated with a running S process in a ;;; buffer. -;;; ;;; GENERAL DISCLAIMER ;;; @@ -27,6 +65,9 @@ ;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA ;;; 02139, USA. ;;; +;;; In short: you may use this code any way you like, as long as you +;;; don't charge money for it, remove this notice, or hold anyone liable +;;; for its results. ;;; OVERVIEW OF S MODE ;;; @@ -35,14 +76,7 @@ ;;; statistics. s-mode is built on top of comint (the general ;;; command interpreter mode written by Olin Shivers), and so ;;; comint.el (or comint.elc) should be either loaded or in your -;;; load path when you invoke it. You might want to put -;;; something like the following in your .emacs file: -;;; -;;; (autoload 'S "~/elisp/S" "" t) -;;; -;;; where "~/elisp/S.el" is the path name of this file. That -;;; way, all you will have to do to get S running is to type -;;; "M-x S" from within emacs. +;;; load path when you invoke it. ;;; ;;; Aside from the general features offered by comint such as ;;; command history editing and job control, inferior S mode @@ -55,6 +89,27 @@ ;;; the variables below that have document strings that start ;;; with an "*"). +;;; INSTALLATION +;;; Save this file in an appropriate directory and put the following +;;; line in your .emacs: +;;; +;;; (autoload 'S "~/elisp/S" "" t) +;;; +;;; where "~/elisp/S.el" is the path name of this file. That +;;; way, all you will have to do to get S running is to type +;;; "M-x S" from within emacs. You may also want to change some +;;; options, by putting lines such as the following in your .emacs: +;;; +;;; (setq inferior-S-program "S") ; command to run S +;;; (setq S-version-running "2.3") ; Running the old version +;;; (setq S-ask-about-display t) ; Ask for an X-display +;;; (setq S-source-directory +;;; (expand-file-name "~/S-Src/")) +;;; (setq S-keep-dump-files t) +;;; ; Make a directory of backup object source files +;;; +;;; See the section "User changeable variables" below for more options. + ;;; GETTING LATER RELEASES OF S MODE ;;; The latest version is available from statlib by sending a ;;; blank message with subject "send index from S" to @@ -64,10 +119,14 @@ ;;; from archive.cis.ohio-state.edu (login anonymous, passwd id) ;;; in directory /pub/gnu/emacs/elisp-archive/as-is/comint.el.Z ;;; This version has been tested and works with (at least) -;;; comint-version 2.02. You probably have copies of comint.el +;;; comint-version 2.03. You probably have copies of comint.el ;;; on your system. Copies are also available from ritter@cs.cmu.edu, ;;; and shivers@cs.cmu.edu. - +;;; +;;; S-mode is also available for anonymous FTP from +;;; attunga.stats.adelaide.edu.au in the directory pub/S-mode. It is +;;; alsa avaliable from the Emacs-lisp archive on +;;; archive.cis.ohio-state.edu. ;;; RELEASE 2.1 INFORMATION ;;; @@ -80,77 +139,278 @@ ;;; commands using them work better. ;;; * we have a version number. ;;; +;;; RELEASE 3.4 INFORMATION +;;; +;;; * Works with version 3.0 S +;;; * Command-line completion of S object names +;;; * Recognition of attached data frames +;;; * Dedicated S Help mode +;;; * Tek graphics support +;;; * Several bugfixes and code cleanups +;;; * Texinfo documentation +;;; ;;; Remaining Bugs: ;;; -;;; * Inferior S mode doesn't do a very good job of offering -;;; defaults when it prompts for names and it is often not -;;; wise to accept them even when they look right. ;;; * It would be nice to use .Last.value when running S+ ;;; * It would be nice to use S VERSION when running S+ +;;; Until the end of August 1992, please report bugs to me at +;;; dsmith@stats.adelaide.edu.au. After this date, mail to that address +;;; will not be answered for some time; please contact Frank Ritter +;;; (Frank_Ritter@SHAMO.SOAR.CS.CMU.EDU) or any of the other authors then +;;; (please CC: to me as well though -- you never know, I might just +;;; answer!) Comments, suggestions, words of praise and large cash +;;; donations are also more than welcome. ;;; Inits and provides ;;;===================================================== ;;; (require 'comint) +(require 'comint-extra) +(autoload 'comint-isearch "comint-isearch" + "Isearch for comint [full documentation when loaded]" t) (provide 'S) +(defconst S-mode-version "3.41" + "Version of S-mode currently loaded.") + ;; this will appear for just a short while, but it's a ;; chance to teach... -(message "Type ^h m for help on s-mode.") +(message + (concat (substitute-command-keys + "Type \\[describe-mode] for help on S-mode version ") + S-mode-version)) -(defvar S-mode-version "2.1" - "Version of S-mode currently loaded.") + -;;; User changable variables +;;; User changeable variables ;;;===================================================== ;;; Users note: Variables with document strings starting ;;; with a * are the ones you can generally change safely, and ;;; may have to upon occasion. -(defvar inferior-S-program "S" +;;; System dependent variables + +(defvar inferior-S-program "Splus" "*Program name for invoking an inferior S.") -(defvar explicit-S-args nil +(defvar inferior-Splus-args nil "*String of arguments passed to the S process on startup if the name of -the S program is `S'.") +the S program is `Splus'.") -(defvar inferior-S-prompt "^\\(\\+\\|[^>]*>\\) *" - "*The regular expression inferior S mode uses for recognizing prompts") +(defvar S-version-running "3.0" + "Version of S being run.") +;;; The value of this variable affects the +;;; default values of the following variables: +;;; +;;; inferior-S-help-command +;;; inferior-S-search-list-command +;;; S-dump-error-re +;;; +;;; Modifications to these variables are made at *load* time (provided, of +;;; course, they have not already been given values), hence changing the +;;; value of S-version-running after this package is loaded will have no +;;; effect. +;;; +;;; Currently the string \"3.0\" is the only value of this variable with +;;; any real meaning; in this case the defaults are set to comply with the +;;; August '91 (3.0) version of S/Splus, defaults which also work for +;;; version 2.3. Any other value than \"3.0\" sets the defaults to comply +;;; with the 1988 version of S/Splus.") +;;; +;;; Please reserve the following values as special: +;;; "3.0" Version 3.0 (August '91) of S/Splus +;;; "2.3" Version 2.3 of S/Splus +;;; "old" Any older version + +(defvar S-plus (assoc inferior-S-program '(("Splus") ("S+"))) + "Set to t if Splus is being used instead of vanilla S") +;;; Used for setting default values of other variables, and hence +;;; has no effect after S.el has been loaded. + +(defvar inferior-S-prompt "\\(\\+\\|[a-zA-Z0-9() ]*>\\) *" + "The regular expression inferior S mode uses for recognizing prompts +Do not anchor to bol with `^'.") + +(defvar inferior-S-primary-prompt "[a-zA-Z0-9() ]*> *" + "Regular expression used by S-mode to detect the primary prompt. +Do not anchor to bol with `^'.") -(defvar S-scratch-file nil - "*The name of the scratch source file that receives dumped objects.") +;;; Initialising the environment -(defvar S-scratch-directory (file-name-as-directory "/tmp") - "*The directory inferior S puts the scratch source files into. It -must end in a slash.") +(defvar S-ask-for-S-directory t + "*If non-nil, the process directory will be requested each time S is run") + +(defvar S-ask-about-display nil + "*If non-nil, asks for a value for the DISPLAY environment +variable, to make X-windows work with S") + +(defvar X-displays-list '("unix:0.0") + "List of strings that are candidates for the DISPLAY environment variable.") (defvar S-directory (file-name-as-directory (getenv "HOME")) - "*The directory S is run from. It must end in a slash.") + "*The directory S is run from. It must end in a slash. +Provided as a default if S-ask-for-S-directory is non-nil.") + +;;; Editing functions + +(defvar S-insert-function-templates t + "*Boolean flag specifying action when editing a non-existent object. +If t, then when the text of a dumped object contains S-dumped-missing-re, +then it will be replaced by S-function-template.") + + ;; By K.Shibayama 5.14.1992 + +(defvar S-indent-level 2 + "*Indentation of S statements with respect to containing block.") + +(defvar S-brace-imaginary-offset 0 + "*Imagined indentation of a S open brace that actually follows a statement.") + +(defvar S-brace-offset 0 + "*Extra indentation for braces, compared with other text in same context.") + +(defvar S-continued-statement-offset 2 + "*Extra indent for lines not starting new statements.") + +(defvar S-continued-brace-offset 0 + "*Extra indent for substatements that start with open-braces. +This is in addition to S-continued-statement-offset.") + +(defvar S-arg-function-offset 2 + "*Extra indent for internal substatements of function `foo' that called +in `arg=foo(...)' form. +If not number, the statements are indented at open-parenthesis following foo.") + +(defvar S-expression-offset 4 + "*Extra indent for internal substatements of `expression' that specified +in `obj <- expression(...)' form. +If not number, the statements are indented at open-parenthesis following +`expression'.") + +(defvar S-auto-newline nil + "*Non-nil means automatically newline before and after braces +inserted in S code.") + +(defvar S-tab-always-indent t + "*Non-nil means TAB in S mode should always reindent the current line, +regardless of where in the line point is when the TAB command is used.") + +(defvar S-default-style 'GNU + "*The default value of S-style") + +(defvar S-style S-default-style + "*The buffer specific S indentation style.") + +;;; Dump files + +(defvar S-source-directory "/tmp/" + "*Directory in which to place dump files. +The directory generated by S-source-directory-generator (if it is +non-nil) is used preferentially, and the value of S-source-directory +is used only of the generated directory can not be written or +created.") + +(defvar S-source-directory-generator nil + "*Function which, when called with no args, will return a directory +name (ending in a slash) into which S objects should be dumped. If this is +nil of the directory does not exist and cannot be created, the value of +S-source-directory is used.") +;;; Possible value: +;;; '(lambda () (file-name-as-directory +;;; (expand-file-name (concat (car S-search-list) "/.Src")))) +;;; This always dumps to a sub-directory (".Src") of the current S +;;; working directory (i.e. first elt of search list) + +(defvar S-dump-filename-template (concat (user-login-name) ".%s.S") + "*Template for filenames of dumped objects. +%s is replaced by the object name.") +;;; This gives filenames like `user.foofun.S', so as not to clash with +;;; other users if you are using a shared directory. Other alternatives: +;;; "%s.S" ; Don't bother uniquifying if using your own directory(ies) +;;; "dump" ; Always dump to a specific filename. This makes it impossible +;;; to edit more than one object at a time, though. +;;; (make-temp-name "scr.") ; Another way to uniquify + +(defvar S-keep-dump-files nil + "*If nil, delete dump files ater use. Otherwise, never delete.") +;;; Boolean flag which determines what to do with the dump files +;;; generated by \\[S-dump-object-into-edit-buffer], as follows: +;;; +;;; If nil: dump files are deleted after each use, and so appear +;;; only transiently. The one exception to this is when a loading error +;;; occurs, in which case the file is retained until the error is +;;; corrected and the file re-loaded. +;;; +;;; If non-nil: dump files are not deleted, and backups are kept +;;; as usual. This provides a simple method for keeping an archive of S +;;; functions in text-file form. +;;; +;;; Auto-save is always enabled in dump-file buffers to enable recovery +;;; from crashes. + +(defvar S-function-template " function( )\n{\n\n}\n" + "Function template used when editing nonexistent objects. +The edit buffer will contain the object name in quotes, followed by +\"<-\", followed by this string.") + +;;; Interacting with the S process + +(defvar S-execute-in-process-buffer nil + "*If non-nil, the S-execute- commands output to the process buffer. +Otherwise, they get their own temporary buffer.") + +(defvar S-eval-visibly-p nil + "*If non-nil, the S-eval- commands display the text to be evaluated +in the process buffer.") + +(defvar S-tek-mode nil + "*Grab Tek Graphics? +Toggle with \\[S-tek-mode-toggle].") + +(defvar S-tek-possible-graph-prompts "Selection: " + "Prompts that might follow TEK graphics. +If S mode seems to lock up when grabbing graphics, it probably means +you need something else in here. Your prompt is assumed: you don't +need to include it. Separate options with \\|") + +(defvar S-tek-pause-for-graphics (not (string= (getenv "TERM") "xterm")) + "If t, wait for a key to be pressed before returning to text mode. +Use this option when graphics and text share the same screen.") + +;;; Help mode + +(defvar S-help-sec-keys-alist + '((?a . "ARGUMENTS:") + (?b . "BACKGROUND:") (?B . "BUGS:") + (?d . "DETAILS:") (?D . "DESCRIPTION:") + (?e . "EXAMPLES:") + (?n . "NOTE:") (?o . "OPTIONAL ARGUMENTS:") (?r . "REQUIRED ARGUMENTS:") + (?R . "REFERENCES:") + (?s . "SIDE EFFECTS:") (?S . "SEE ALSO:") (?u . "USAGE:") (?v . "VALUE:")) + "Alist of (key . string) pairs for use in section searching.") +;;; `key' indicates the keystroke to use to search for the section heading +;;; `string' in an S help file. `string' is used as part of a +;;; regexp-search, and so specials should be quoted. + +;;; Hooks (defvar S-mode-hook '() "*Hook for customizing S mode each time it is entered.") (defvar S-mode-load-hook '() - "*Hook to call each time S mode is loaded.") + "*Hook to call when S.el is loaded.") (defvar S-pre-run-hook nil - "*Hook to call before starting up S, good for setting up your directory.") - + "*Hook to call before starting up S. +Good for setting up your directory.") ;; You can put something like: -;; (setq S-directory (file-name-as-directory (concat (getenv "HOME") "/S")))) +;; (setq S-directory (file-name-as-directory (concat (getenv "HOME") "/S"))) ;; in your ~/.emacs file and S will always start up in your ~/S directory. ;; Alternatively, you can get S to start up in the directory you start ;; Emacs from by putting this in your .emacs: ;; (setq S-pre-run-hook '((lambda () (setq S-directory default-directory)))) -;(defvar use-S-set-dir-file! t -; "*If t (default), calling S or run-S sets up a shell file, that sets (cd's) -;the directory for S, and then calls the inferior-S-program. If nil, just -;calls the inferior-S-program. If you are already calling a shell script -;to start up S, this should not hurt you either way.") - - ;;; System variables @@ -160,43 +420,83 @@ must end in a slash.") (defvar S-change-sp-regexp "\\(attach(\\([^)]\\|$\\)\\|detach(\\|collection(\\|library(\\)" - "The regular expression for matching the S commands that change the -search path.") + "The regexp for matching the S commands that change the search path.") (defvar S-function-pattern - "^[^\n\t]*\\(<-\\|_\\)[ \n]*function[ \t]*(" - "The regular expression for matching the beginning of an S -function.") + (concat + "\\(" ; EITHER + "\\s\"" ; quote + "\\(\\sw\\|\\s_\\)+" ; symbol + "\\s\"" ; quote + "\\s-*\\(<-\\|_\\)\\(\\s-\\|\n\\)*" ; whitespace, assign, whitespace/nl + "function\\s-*(" ; function keyword, parenthesis + "\\)\\|\\(" ; OR + "\\<\\(\\sw\\|\\s_\\)+" ; symbol + "\\s-*\\(<-\\|_\\)\\(\\s-\\|\n\\)*" ; whitespace, assign, whitespace/nl + "function\\s-*(" ; function keyword, parenthesis + "\\)") + "The regular expression for matching the beginning of an S function.") (defvar S-source-modes '(S-mode) - "A list of modes used to determine if a buffer contains S source code. -If a file is loaded into a buffer that is in one of these major modes, it -is considered an S source file. The function S-load-file uses this to -determine defaults.") + "A list of modes used to determine if a buffer contains S source code.") +;;; If a file is loaded into a buffer that is in one of these major modes, it +;;; is considered an S source file. The function S-load-file uses this to +;;; determine defaults. (defvar inferior-S-load-command "source(\"%s\")\n" - "Format-string for building the S command to load a file. -This format string should use %s to substitute a file name -and should result in an S expression that will command the inferior S -to load that file.") + "Format-string for building the S command to load a file.") +;;; This format string should use %s to substitute a file name +;;; and should result in an S expression that will command the inferior S +;;; to load that file. (defvar inferior-S-dump-command "dump(\"%s\",file=\"%s\")\n" - "Format-string for building the S command to dump an object into a file. -This format string should use %s to substitute an object and a file name.") - -(defvar inferior-S-help-command "help(\"%s\")\n" - "Format-string for building the S command to ask for help on an object. -This format string should use %s to substitute an object name.") - -(defvar inferior-S-search-list-command "search()\n" - "S command that prints out the search list---the list of -directories and (recursive) objects that S uses when it searches for -objects.") -;changed from "attach()\n" + "Format-string for building the S command to dump an object into a file.") +;;; Use first %s to substitute an object name +;;; second %s substitutes the dump file name. + +(defvar inferior-S-help-command + (if S-plus + "help(\"%s\",pager=\"cat\",window=F)\n" + "help(\"%s\")\n") + "Format-string for building the S command to ask for help on an object.") +;;; This format string should use %s to substitute an object name. + +(defvar inferior-S-search-list-command "search()\n" + "S command that prints out the search list.") +;;; i.e. The list of directories and (recursive) objects that S uses when +;;; it searches for objects. + +(defvar inferior-S-names-command "names(%s)\n" + "Format string for S command to extract names from an object.") +;;; %s is replaced by the object name -- usually a list or data frame + +(defvar inferior-S-objects-command + (if (string= S-version-running "3.0") + "objects(%d)" + "ls()") + "Format string for S command to get a list of objects at position %d") +;;; Don't include a newline at the end! Used in S-execute-objects + +(defvar S-dumped-missing-re "\nDumped\n\\'" + "If a dumped object's buffer matches this re, then it is replaced +by S-function-template.") + +(defvar S-dump-error-re + (if (string= S-version-running "3.0") "\nDumped\n\\'" "[Ee]rror") + "Regexp used to detect an error when loading a file.") + +(defvar S-error-buffer-name " *S-errors*" + "Name of buffer to keep error messages in.") + +(defvar S-loop-timeout 20000 + "Integer specifying how many loops S-mode will wait for the prompt for +before signalling an error.") (defvar S-search-list nil - "The list of directories and (recursive) objects to search for S -objects.") + "The list of directories and (recursive) objects to search for S objects.") + +(defvar S-sl-modtime-alist nil + "Alist of modtimes for all S directories accessed this session.") (defvar S-sp-change nil "This symbol flags a change in the S search path.") @@ -205,16 +505,50 @@ objects.") "This symbol saves the (directory . file) pair used in the last S-load-file command. Used for determining the default in the next one.") -;(defvar internal-S-file-name ".run-s" -; "This is the file name where S sets up the shell script to set the directory -;and run S.") -; -;(defvar modified-S-program nil -; "The name for S that includes the change directory if requested.") +(defvar inferior-S-get-prompt-command "options()$prompt\n" + "Command to find the value of the current S prompt.") + +(defvar S-temp-buffer-p nil + "*Flags whether the current buffer is a temporary buffer created by S-mode. +Such buffers will be killed by \\[S-quit] or \\[S-cleanup]. +Source buffers and help buffers have this flag set. +This is a buffer-local variable.") +(make-variable-buffer-local 'S-temp-buffer-p) + +(defvar S-local-variables-string " + +# Local Variables: +# mode:S +# S-temp-buffer-p:t +# End: +") + +(defvar S-style-alist +'((GNU (S-indent-level . 2) + (S-continued-statement-offset . 2) + (S-brace-offset . 0) + (S-arg-function-offset . 4) + (S-expression-offset . 2)) + (BSD (S-indent-level . 8) + (S-continued-statement-offset . 8) + (S-brace-offset . -8) + (S-arg-function-offset . 0) + (S-expression-offset . 8)) + (K&R (S-indent-level . 5) + (S-continued-statement-offset . 5) + (S-brace-offset . -5) + (S-arg-function-offset . 0) + (S-expression-offset . 5)) + (C++ (S-indent-level . 4) + (S-continued-statement-offset . 4) + (S-brace-offset . -4) + (S-arg-function-offset . 0) + (S-expression-offset . 4))) +"Predefined formatting styles for S code") + +(defvar S-tek-simple-prompt nil + "Explicit version of primary S prompt.") -(defvar internal-S-file-name-header - "# This file is how s-mode calls S. It is rebuilt each time run-s or S -# is called by s-mode. You may delete it if you wish.") ;;; S-mode helper functions and code @@ -225,10 +559,26 @@ S-load-file command. Used for determining the default in the next one.") (if inferior-S-mode-map nil (setq inferior-S-mode-map (full-copy-sparse-keymap comint-mode-map)) - (define-key inferior-S-mode-map "\r" 'S-send-input) - (define-key inferior-S-mode-map "\C-cl" 'S-load-file) - (define-key inferior-S-mode-map "\C-cd" 'S-dump-object-into-scratch) - (define-key inferior-S-mode-map "\C-ch" 'S-display-help-on-object)) + (define-key inferior-S-mode-map "\r" 'inferior-S-send-input) + (define-key inferior-S-mode-map "\eP" 'comint-msearch-input) + (define-key inferior-S-mode-map "\eN" 'comint-psearch-input) + (define-key inferior-S-mode-map "\C-c\C-b" 'comint-msearch-input-matching) + (define-key inferior-S-mode-map "\eS" 'comint-next-similar-input) + (define-key inferior-S-mode-map "\er" 'comint-isearch) + (define-key inferior-S-mode-map "\C-c\C-l" 'S-load-file) + (define-key inferior-S-mode-map "\C-x`" 'S-parse-errors) + (define-key inferior-S-mode-map "\C-c\C-d" 'S-dump-object-into-edit-buffer) + (define-key inferior-S-mode-map "\C-c\C-h" 'S-display-help-on-object) + (define-key inferior-S-mode-map "\C-c\C-t" 'S-tek-mode-toggle) + (define-key inferior-S-mode-map "\C-c\C-q" 'S-quit) + (define-key inferior-S-mode-map "\C-c\C-e" 'S-execute) + (define-key inferior-S-mode-map "\C-c\C-s" 'S-execute-search) + (define-key inferior-S-mode-map "\C-c\C-x" 'S-execute-objects) + (define-key inferior-S-mode-map "\C-c\C-a" 'S-execute-attach) + (define-key inferior-S-mode-map "\C-c\C-z" 'S-abort) ; these mask in + (define-key inferior-S-mode-map "\C-c\C-o" 'S-kill-output) ; comint-m-map + (define-key inferior-S-mode-map "\C-c\C-v" 'S-view-at-bottom) + (define-key inferior-S-mode-map "\t" 'S-complete-object-name)) (defvar S-mode-syntax-table nil "Syntax table for S-mode.") (if S-mode-syntax-table @@ -237,78 +587,105 @@ S-load-file command. Used for determining the default in the next one.") (modify-syntax-entry ?# "<" S-mode-syntax-table) ; now an open comment (modify-syntax-entry ?\n ">" S-mode-syntax-table) ; close comment (modify-syntax-entry ?_ "." S-mode-syntax-table) + (modify-syntax-entry ?. "w" S-mode-syntax-table) ; making S names same as + (modify-syntax-entry ?$ "w" S-mode-syntax-table) ; words makes coding easier (modify-syntax-entry ?* "." S-mode-syntax-table) + (modify-syntax-entry ?< "." S-mode-syntax-table) + (modify-syntax-entry ?> "." S-mode-syntax-table) (modify-syntax-entry ?/ "." S-mode-syntax-table)) + (defvar inferior-S-mode-hook '() - "*Hook for customizing inferior S mode") + "*Hook for customizing inferior S mode. +Called after inferior-S-mode is entered and variables have been initialised.") + + +;;; +;;; Starting up +;;; (defun S () "Run an inferior S process, input and output via buffer *S*. If there is a process already running in *S*, just switch to that buffer. Takes the program name from the variable inferior-S-program. -The S program name is used to make a symbol name such as `explicit-S-args'. +The S program name is used to make a symbol name such as `inferior-S-args'. If that symbol is a variable its value is used as a string of arguments when invoking S. \(Type \\[describe-mode] in the process buffer for a list of commands.)" (interactive) (if (not (comint-check-proc "*S*")) (let* ((symbol-string - (concat "explicit-" inferior-S-program "-args")) + (concat "inferior-" inferior-S-program "-args")) (switches-symbol (intern-soft symbol-string)) (switches (if (and switches-symbol (boundp switches-symbol)) (symbol-value switches-symbol)))) - ;(if use-S-set-dir-file! - ; (make-s-run-file) - ; (setq modified-S-program inferior-S-program)) - (set-buffer + (run-hooks 'S-pre-run-hook) + (if S-ask-for-S-directory (S-set-directory)) + (if S-ask-about-display (S-set-display)) + (set-buffer (if switches - (make-S-comint switches) - (make-S-comint))) + (inferior-S-make-comint switches) + (inferior-S-make-comint))) (inferior-S-mode) - (wait-for-S-prompt) + (inferior-S-wait-for-prompt) (goto-char (point-max)) - (get-S-search-list))) - (pop-to-buffer "*S*")) + (setq S-sl-modtime-alist nil) + (S-tek-get-simple-prompt) + (S-get-search-list))) + (switch-to-buffer "*S*")) + +(defun S-set-directory nil + "Interactively set S-directory." + (setq S-directory + (expand-file-name + (file-name-as-directory + (read-file-name + "From which directory? " S-directory S-directory t))))) + +(defun S-set-display nil + "Interactively set DISPLAY variable for S process" + (let* ((matches (append (mapcar + '(lambda (envelt) + (if (string-match "^DISPLAY=\\(.*\\)$" envelt) + (substring envelt (match-beginning 1) (match-end 1)))) + process-environment) + (list (getenv "DISPLAY")))) + (initial (eval (cons 'or matches)))) + (setq process-environment + (comint-update-env + process-environment + (list (concat + "DISPLAY=" + (completing-read + "Which X-display? " + (mapcar 'list X-displays-list) + nil + nil + initial))))))) ;;; define two commands consistent with other comint modes, run-s & ;;; run-S. (fset 'run-s (fset 'run-S (symbol-function 'S))) -;;; this is inelegant, but will work for now -;(defun make-s-run-file () -; (run-hooks 'S-pre-run-hook) -; (setq file-name -; (concat (file-name-as-directory (getenv "HOME")) internal-S-file-name)) -; (if (file-readable-p file-name) -; (delete-file file-name)) -; (switch-to-buffer internal-S-file-name) -; (erase-buffer) -; (insert "#!/bin/sh\n") -; (insert internal-S-file-name-header) -; (insert "\n# so cd to the user's S-directory") -; (insert (format "\ncd %s" S-directory)) -; (insert "\n# and then run his/her inferior-S-program") -; (insert (format "\n%s\n" inferior-S-program)) -; (write-file file-name) -; (kill-buffer internal-S-file-name) -; (shell-command (format "chmod +x %s" file-name)) -; (setq modified-S-program file-name)) - (defun inferior-S-mode () "Major mode for interacting with an inferior S process. Runs an S interactive job as a subprocess of Emacs, with I/O through an Emacs buffer. Variable inferior-S-program controls which S is run. +Commands are sent to the S process by typing them, and pressing +\\[inferior-S-send-input]. Pressing \\[S-complete-object-name] completes known +object names. Other keybindings for this mode are: + \\{inferior-S-mode-map} -Do not type \\[S-dump-object-into-scratch] or \\[S-display-help-on-object] -when you are in the middle of delivering a multi-line command to S and S is -prompting you with its secondary prompt. It's ok to do this if S hasn't -seen the initial lines yet---that is, if you ended those lines with -something other than a \"send input\" command (usually bound to RETURN). +When editing S objects, the use of \\[S-load-file] is advocated. +S-load-file keeps source files (if S-keep-dump-files is non-nil) in +the directory specified by S-source-directory-generator, with the +filename chosen according to S-dump-filename-template. When a file is +loaded, S-mode parses error messages and jumps to the appropriate file +if errors occur. The S-eval- commands do not do this. Customization: Entry to this mode runs the hooks on comint-mode-hook and inferior-S-mode-hook (in that order). @@ -320,17 +697,17 @@ S source. The key bindings of these commands can be found by typing S-eval-buffer sends the current buffer to the S process. S-eval-function sends the current function to the S process. S-eval-line sends the current line to the S process. - beginning-of-S-function and end-of-S-function move the point to + S-beginning-of-function and S-end-of-function move the point to the beginning and end of the current S function. - switch-to-S switches the current buffer to the S process buffer. - switch-to-end-of-S switches the current buffer to the S process + S-switch-to-S switches the current buffer to the S process buffer. + S-switch-to-end-of-S switches the current buffer to the S process buffer and puts point at the end of it. S-eval-region-and-go, S-eval-buffer-and-go, S-eval-function-and-go, and S-eval-line-and-go switch to the S process buffer after sending their text. - S-dump-object-into-scratch moves an S object into a temporary file + S-dump-object-into-edit-buffer moves an S object into a temporary file and buffer for editing S-load-file sources a file of commands to the S process. @@ -346,18 +723,23 @@ If you accidentally suspend your process, use \\[comint-continue-subjob] to continue it." (interactive) (comint-mode) - (setq comint-prompt-regexp inferior-S-prompt) + (setq comint-prompt-regexp (concat "^" inferior-S-prompt)) (setq major-mode 'inferior-S-mode) (setq mode-name "Inferior S") (setq mode-line-process '(": %s")) (use-local-map inferior-S-mode-map) (set-syntax-table S-mode-syntax-table) (setq comint-input-sentinel 'S-search-path-tracker) + (setq comint-get-old-input 'inferior-S-get-old-input) + (make-local-variable 'scroll-step) + (setq scroll-step 4) + (make-local-variable 'input-ring-size) + (setq input-ring-size 50) (run-hooks 'inferior-S-mode-hook)) ;;; This function is a modification of make-comint from the comint.el ;;; code of Olin Shivers. -(defun make-S-comint (&rest switches) +(defun inferior-S-make-comint (&rest switches) (let* ((name "S") (buffer (get-buffer-create (concat "*" name "*"))) (proc (get-buffer-process buffer))) @@ -371,199 +753,417 @@ to continue it." (comint-exec buffer name inferior-S-program nil switches))) buffer)) -(defun S-send-input () +(defun inferior-S-send-input () + "Sends the command on the current line to the S process." (interactive) (comint-send-input) (if (and S-sp-change - (wait-for-S-prompt)) + (inferior-S-wait-for-prompt)) (progn - (get-S-search-list) - (setq S-sp-change nil)))) - -(defun wait-for-S-prompt () + (S-get-search-list) + (setq S-sp-change nil)) + ;; Is this TEK graphics output? + (if S-tek-mode + (progn + (require 'S-tek) + (S-tek-snarf-graphics))))) + +(defun inferior-S-get-old-input () + "Returns the S command surrounding point." + (save-excursion + (beginning-of-line) + (if (not (looking-at inferior-S-prompt)) + (S-error "No command on this line.")) + (if (looking-at inferior-S-primary-prompt) nil + (re-search-backward (concat "^" inferior-S-primary-prompt))) + (comint-skip-prompt) + (let (command + (beg (point))) + (end-of-line) + (setq command (buffer-substring beg (point))) + (forward-line 1) + (while (and (looking-at inferior-S-prompt) + (not (looking-at inferior-S-primary-prompt))) + ;; looking at secondary prompt + (comint-skip-prompt) + (setq beg (point)) + (end-of-line) + (setq command (concat command " " (buffer-substring beg (point)))) + (forward-line 1)) + command))) + +(defun S-error (msg) + "Something bad has happened. Display the S buffer, and cause an error +displaying MSG." + (display-buffer (process-buffer (get-process "S"))) + (error msg)) + +(defun inferior-S-wait-for-prompt () + "Wait until the S process is ready for input." (let* ((cbuffer (current-buffer)) (sprocess (get-process "S")) (sbuffer (process-buffer sprocess)) - r) + r + (timeout 0)) (set-buffer sbuffer) (while (progn - (accept-process-output sprocess) - (goto-char (point-max)) - (beginning-of-line) - (setq r (looking-at inferior-S-prompt)) - (not (or r (looking-at ".*\\?\\s *"))))) + (if (not (eq (process-status sprocess) 'run)) + (S-error "S process has died unexpectedly.") + (if (> (setq timeout (1+ timeout)) S-loop-timeout) + (S-error "Timeout waiting for prompt. Check inferior-S-prompt or S-loop-timeout.")) + (accept-process-output) + (goto-char (point-max)) + (beginning-of-line) + (setq r (looking-at inferior-S-prompt)) + (not (or r (looking-at ".*\\?\\s *")))))) (goto-char (point-max)) (set-buffer cbuffer) (symbol-value r))) -(defun S-dump-object-into-scratch (object) - "Dump the S object into a file (and buffer) for editing." - (interactive (find-S-object "Object to edit: ")) - (let* ((filename (concat S-scratch-directory - (or S-scratch-file (make-temp-name "scr.")))) +(defun S-dump-object-into-edit-buffer (object) + "Edit an S object in its own buffer. Without a prefix argument, +this simply finds the file pointed to by S-dump-filename. If this file +does not exist, or if a prefix argument is given, a dump() command is +sent to the S process to generate the source buffer." + (interactive (S-read-object-name "Object to edit: ")) + (let* ((filename (concat (if S-source-directory-generator + (funcall S-source-directory-generator) + S-source-directory) + (format S-dump-filename-template object))) (complete-dump-command (format inferior-S-dump-command object filename)) - old-scratch-buffer) - (command-to-S complete-dump-command) - (if (setq old-scratch-buffer (get-file-buffer filename)) - (kill-buffer old-scratch-buffer)) ;make sure we start fresh - (find-file-other-window filename) - (S-mode) + (old-buff (get-file-buffer filename))) + (if S-source-directory-generator + (let ((the-dir (file-name-directory filename))) + ;; If the directory doesn't exist, create if possible and approved. + (if (not (file-writable-p filename)) ; Can't create file + (if (and (not (file-exists-p the-dir)) ; No such directory + (file-writable-p ; Can we create dir in parent? + (file-name-directory (directory-file-name the-dir))) + (y-or-n-p ; Approved + (format "Directory %s does not exist. Create it? " the-dir))) ; and we want to create it + (make-directory (directory-file-name the-dir)) + (setq filename (concat S-source-directory + (format S-dump-filename-template object))))))) + ;; Try and find a buffer or filename before asking S + (catch 'found-text + (if (not current-prefix-arg) + (cond + (old-buff + (pop-to-buffer old-buff) + (message "Popped to edit buffer.") + (throw 'found-text nil)) + ((file-exists-p filename) + (find-file-other-window filename) + (message "Read %s" filename) + (throw 'found-text nil)))) + (S-command complete-dump-command) + (let ((old-buff (get-file-buffer filename))) + (if old-buff + (kill-buffer old-buff))) ;make sure we start fresh + ;; Generate a buffer with the dumped data + (find-file-other-window filename) + (S-mode) + (auto-save-mode 1) ; Auto save in this buffer + (setq S-temp-buffer-p t) ; Flag as a temp buffer + (if S-insert-function-templates + (progn + (goto-char (point-max)) + (if (re-search-backward S-dumped-missing-re nil t) + (replace-match S-function-template t t)) + (goto-char (point-min)))) ;It might be nice to go between braces here + ;; Insert the local variables stuff + (save-excursion + (goto-char (point-max)) + (insert S-local-variables-string) + (if S-keep-dump-files nil + (set-buffer-modified-p nil))) + (message "Dumped in %s" filename) + (if S-keep-dump-files nil + (delete-file filename))) ; In case buffer is killed (setq S-prev-load-dir/file - (cons (file-name-directory filename) - (file-name-nondirectory filename))))) + (cons (file-name-directory filename) + (file-name-nondirectory filename))))) -(defun find-S-object (p-string) - (let* ((default (find-S-object-default)) +(defun S-read-object-name (p-string) + (let* ((default (S-read-object-name-default)) (prompt-string (if default (format "%s(default %s) " p-string default) p-string)) - (S-object-list (get-S-object-list)) + (S-object-list (S-get-object-list)) (spec (completing-read prompt-string S-object-list))) (list (cond ((string= spec "") default) (t spec))))) -(defun find-S-object-default () - (save-excursion - (while (looking-at "\\sw\\|\\s.") - (forward-char 1)) - (if (re-search-backward "\\sw\\|\\s." nil t) - (progn (forward-char 1) - (buffer-substring (point) - (progn (forward-sexp -1) - (while (looking-at "\\s'") - (forward-char 1)) - (point)))) - nil))) - -(defun get-S-search-list () +(defun S-read-object-name-default () + (save-excursion + ;; The following line circumvents an 18.57 bug in following-char + (if (eobp) (backward-char 1)) ; Hopefully buffer is not empty! + ;; Get onto a symbol + (catch 'nosym ; bail out if there's no symbol at all before point + (while (/= (char-syntax (following-char)) ?w) + (if (bobp) (throw 'nosym nil) (backward-char 1))) + (let* + ((end (progn (forward-sexp 1) (point))) + (beg (progn (backward-sexp 1) (point)))) + (buffer-substring beg end))))) + +(defun S-object-names (dir) + "Return alist of S object names in directory (or object) DIR" + (if (string-match "^/" dir) + (mapcar 'list (directory-files dir)) + ;;It might be an object name; try to get names + (let ((tbuffer (generate-new-buffer "names-list")) + (objname dir) + names) + (save-excursion + (set-buffer tbuffer) + (buffer-flush-undo tbuffer) + (S-command (format inferior-S-names-command objname) tbuffer) + (goto-char (point-min)) + (if (not (looking-at "\\s-*\\[1\\]")) + (setq names nil) + (goto-char (point-max)) + (while (re-search-backward "\"\\([^\"]*\\)\"" nil t) + (setq names (cons (buffer-substring (match-beginning 1) + (match-end 1)) names)))) + (kill-buffer tbuffer)) + (mapcar 'list names)))) + +(defun S-resynch nil +"Reread all directories/objects in S-search-list to form completions." + (interactive) + (setq S-sl-modtime-alist nil) + (S-get-search-list)) + +(defun S-extract-onames-from-alist (dir) +"Extract the object names for directory (or object) DIR from S-sl-modtime-alist +generating a new set if the directory has been recently modified." + (let* ((assoc-res (assoc dir S-sl-modtime-alist)) + (data-cell (cdr assoc-res)) + (last-mtime (car data-cell)) + (new-mtime (S-dir-modtime dir)) + (old-objs (cdr data-cell))) + (if (equal new-mtime last-mtime) old-objs + (setcar data-cell new-mtime) + (setcdr data-cell (S-object-names dir))))) + +(defun S-dir-modtime (dir) +"Return the last modtime if dir is a directory, and nil otherwise." +;; Attached dataframes return a modtime of nil. It probably wouldn't be +;; too difficult to find the modtime of the actual object by searching for +;; it along S-search-list, but one hardly ever modifies dataframes after +;; they're attached, and I couldn't be bothered anyway. + (if (string-match "^/" dir) + (nth 5 (file-attributes dir)))) + +(defun S-get-search-list () "Get the list of directories and (recursive) objects that S searches when it looks for objects." + (save-excursion (let ((tbuffer (generate-new-buffer "search-list")) + dir-assoc dir) (setq S-search-list nil) (buffer-flush-undo tbuffer) (set-buffer tbuffer) - (command-to-S inferior-S-search-list-command tbuffer) + (S-command inferior-S-search-list-command tbuffer) (goto-char (point-max)) (while (re-search-backward "\"\\([^\"]*\\)\"" nil t) (setq dir (buffer-substring (match-beginning 1) (match-end 1))) - (if (string-match "^[^/]" dir) + (if (and (string-match "^[^/]" dir) + (file-directory-p (concat S-directory dir))) (setq dir (concat S-directory dir))) - (setq S-search-list (cons dir S-search-list))) - (kill-buffer tbuffer))) - -(defun get-S-object-list () - "Return the alist of current S object names (suitable for use with -completing-read)." - (get-S-object-list-r S-search-list)) - -(defun get-S-object-list-r (s-list) - "Return the alist of current S object names, recursive version. S-LIST -is the search list of directories (or objects) for S." + (setq S-search-list (cons dir S-search-list)) + (setq dir-assoc (assoc dir S-sl-modtime-alist)) + (if (not dir-assoc) + (let (conselt) + (setq conselt (cons dir + (cons (S-dir-modtime dir) + (S-object-names dir)))) + (setq S-sl-modtime-alist (cons conselt S-sl-modtime-alist))))) + (kill-buffer tbuffer)))) + +(defun S-get-object-list () + "Return the alist of current S object names." +;;; suitable for use with completing-read + (S-get-object-list-r S-search-list)) + +(defun S-get-object-list-r (s-list) + "Return the alist of current S object names, recursive version. +S-LIST is the search list of directories (or objects) for S." (let* ((dir (car s-list)) (dir-list (cdr s-list))) (if (null dir) nil - (append (if (file-directory-p dir) - (mapcar 'list (directory-files dir))) - (get-S-object-list-r dir-list))))) - -;(defun command-to-S (com &optional buf) -; "Send the S process a COMMAND and delete the output from the S process -;buffer. If an optional second argument BUFFER exists save the output -;there. (BUFFER cannot be the S process buffer.)" -; (let* ((cbuffer (current-buffer)) -; (sprocess (get-process "S")) -; (sbuffer (process-buffer sprocess)) -; place-holder -; last) -; (set-buffer sbuffer) -; (setq place-holder (point-marker)) -; (kill-region (process-mark sprocess) (point-max)) -; (setq last (point-max)) -; (process-send-string sprocess com) -; (while (progn -; (accept-process-output sprocess) -; (goto-char (point-max)) -; (beginning-of-line) -; (or (= (point-max) last) -; (not (looking-at inferior-S-prompt))))) -; (if buf -; (append-to-buffer buf last (point))) -; (delete-region last (point-max)) -; (yank) ;possible command in process -; (goto-char (marker-position place-holder)) -; (set-buffer cbuffer))) - -;; revised form that uses a register -(defun command-to-S (com &optional buf) - "Send the S process a COMMAND and delete the output from the S process -buffer. If an optional second argument BUFFER exists save the output -there. (BUFFER cannot be the S process buffer.)" + (append (S-extract-onames-from-alist dir) + (S-get-object-list-r dir-list))))) + +(defun S-command (com &optional buf visible) + "Send the S process command COM and delete the output +from the S process buffer. If an optional second argument BUF exists +save the output in that buffer. If optional third arg VISIBLE is +non-nil, both the command and the output appear in the S process +buffer." (let* ((cbuffer (current-buffer)) (sprocess (get-process "S")) - (sbuffer (process-buffer sprocess)) - place-holder - last) + sbuffer + start-of-output + point-holder) + (if sprocess nil (error "No S process running!")) + (setq sbuffer (process-buffer sprocess)) (set-buffer sbuffer) - (setq place-holder (point-marker)) - ;; copy partial, unentered code to somewhere safer, deleting it for now - (copy-to-register ?S (process-mark sprocess) (point-max) t) - (setq last (point-max)) + (setq point-holder (point-marker)) + (goto-char (marker-position (process-mark sprocess))) + (beginning-of-line) + (if (looking-at inferior-S-primary-prompt) nil + (goto-char (marker-position point-holder)) + (S-error + "S process not ready. Finish your command before trying again.")) + (if visible + (progn + (goto-char (marker-position (process-mark sprocess))) + (insert-before-markers com) )) + (setq start-of-output (marker-position (process-mark sprocess))) (process-send-string sprocess com) (while (progn (accept-process-output sprocess) - (goto-char (point-max)) + (goto-char (marker-position (process-mark sprocess))) (beginning-of-line) - (or (= (point-max) last) - (not (looking-at inferior-S-prompt))))) + (if (< (point) start-of-output) (goto-char start-of-output)) + (not (looking-at inferior-S-primary-prompt)))) (if buf - (append-to-buffer buf last (point))) - (delete-region last (point-max)) - (if (get-register ?S) - (insert-register ?S)) ;possible command in process - (goto-char (marker-position place-holder)) + (append-to-buffer buf start-of-output (point))) + (if visible (goto-char (marker-position (process-mark sprocess))) + (delete-region start-of-output + (marker-position (process-mark sprocess))) + (goto-char (marker-position point-holder))) (set-buffer cbuffer))) -(defun S-display-help-on-object (object) - "Display the help page for OBJECT in the *Help* buffer." - (interactive (find-S-object "Help on: ")) - (let (tbuffer) - (pop-to-buffer "*Help*") - (setq tbuffer (current-buffer)) - (delete-region (point-min) (point-max)) - (command-to-S (format inferior-S-help-command object) tbuffer) - (nuke-S-help-bs) - (goto-char (point-min)))) +(defun S-eval-visibly (text &optional invisibly) + "Evaluate TEXT in the S process buffer as if it had been typed in. +If optional secod arg INVISIBLY is non-nil, don't echo commands. If +if is a string, just include that string. +Waits for prompt after each line of input, so won't break on large texts." + (let* ((cbuffer (current-buffer)) + (sprocess (get-process "S")) + (sbuffer (process-buffer sprocess)) + start-of-output + com pos) + (set-buffer sbuffer) + (goto-char (marker-position (process-mark sprocess))) + (setq comint-last-input-end (point-marker)) + (if (stringp invisibly) + (insert-before-markers (concat "*** " invisibly " ***\n"))) + (while (> (length text) 0) + (setq pos (string-match "\n\\|$" text)) + (setq com (concat (substring text 0 pos) "\n")) + (setq text (substring text (min (length text) (1+ pos)))) + (goto-char (marker-position (process-mark sprocess))) + (if invisibly nil (insert-before-markers com)) + (setq start-of-output (marker-position (process-mark sprocess))) + (process-send-string sprocess com) + (while (progn + (accept-process-output sprocess) + (goto-char (marker-position (process-mark sprocess))) + (beginning-of-line) + (if (< (point) start-of-output) (goto-char start-of-output)) + (not (looking-at inferior-S-prompt))))) + (goto-char (marker-position (process-mark sprocess))) + (set-buffer cbuffer))) -;;; This function is a modification of nuke-nroff-bs in man.el from the -;;; standard emacs 18 lisp library. -(defun nuke-S-help-bs () - (interactive "*") - ;; Nuke underlining and overstriking (only by the same letter) - (goto-char (point-min)) - (while (search-forward "\b" nil t) - (let* ((preceding (char-after (- (point) 2))) - (following (following-char))) - (cond ((= preceding following) - ;; x\bx - (delete-char -2)) - ((= preceding ?\_) - ;; _\b - (delete-char -2)) - ((= following ?\_) - ;; \b_ - (delete-region (1- (point)) (1+ (point))))))) - ;; Crunch blank lines - (goto-char (point-min)) - (while (re-search-forward "\n\n\n\n*" nil t) - (replace-match "\n\n")) - ;; Nuke blanks lines at start. - (goto-char (point-min)) - (skip-chars-forward "\n") - (delete-region (point-min) (point))) +(defun S-execute (command &optional invert buff message) + "Send a command to the S process. +A newline is automatically added to COMMAND. Prefix arg (or second arg INVERT) +means invert the meaning of S-execute-in-process-buffer. If INVERT is 'buffer, +output is forced to go to the process buffer. +If the output is going to a buffer, name it *BUFF*. This buffer is erased +before use. Optional fourth arg MESSAGE is text to print at the top of the +buffer (defaults to the command if BUFF is not given.)" + (interactive "sCommand: \nP") + (let ((the-command (concat command "\n")) + (buff-name (concat "*" (or buff "S-output") "*")) + (in-pbuff (if invert (or (eq invert 'buffer) + (not S-execute-in-process-buffer)) + S-execute-in-process-buffer))) + (if in-pbuff + (S-eval-visibly the-command) + (with-output-to-temp-buffer buff-name + (if message (princ message) + (if buff nil + ;; Print the command in the buffer if it has not been + ;; given a special name + (princ "> ") + (princ the-command))) + (S-command the-command (get-buffer buff-name) nil)) + (save-excursion + (set-buffer (get-buffer buff-name)) + (setq S-temp-buffer-p t))))) + +(defun S-execute-in-tb nil + "Like S-execute, but always evaluates in temp buffer." + (interactive) + (let ((S-execute-in-process-buffer nil)) + (call-interactively 'S-execute))) + +(defun S-execute-objects (posn) + "Send the objects() command to the S process. +By default, gives the objects at position 1. +A prefix argument toggles the meaning of S-execute-in-process-buffer. +A prefix argument of 2 or more means get objects for that position. +A negative prefix argument gets the objects for that position + and toggles S-execute-in-process-buffer as well." + (interactive "P") + (let* ((num-arg (if (listp posn) + (if posn -1 1) + (prefix-numeric-value posn))) + (the-posn (if (< num-arg 0) (- num-arg) num-arg)) + (invert (< num-arg 0)) + (the-command (format inferior-S-objects-command the-posn)) + (the-message (concat ">>> Position " + the-posn + " (" + (nth (1- the-posn) S-search-list) + ")\n"))) + (S-execute the-command invert "S objects" the-message))) + +(defun S-execute-search (invert) + "Send the search() command to the S process." + (interactive "P") + (S-execute "search()" invert "S search list")) + +(defun S-execute-attach (dir &optional posn) + "Attach a directory in the S process with the attach() command. +When used interactively, user is prompted for DIR to attach and +prefix argument is used for POSN (or 2, if absent.) +Doesn't work for data frames." + (interactive "DAttach directory: \nP") + (S-execute (concat "attach(\"" + (directory-file-name (expand-file-name dir)) + "\"" + (if posn (concat "," (prefix-numeric-value posn))) + ")") 'buffer)) + +(defun S-view-at-bottom () + "Move to the end of the buffer, and place cursor on bottom line of window." + (interactive) + (goto-char (point-max)) + (recenter -1)) + +(defun S-kill-output () + "Kill all output from last S command." + ;; A version of comint-kill-output that doesn't nuke the prompt. + (interactive) + (let* ((sprocess (get-process "S")) + (pmark (process-mark sprocess)) + (oldpoint (point-marker))) + (goto-char pmark) + (re-search-backward inferior-S-primary-prompt) + (kill-region comint-last-input-end (point)) + (insert "*** output flushed ***\n") + (goto-char oldpoint) + (recenter -1))) (defun S-load-file (filename) "Load an S source file into an inferior S process." @@ -571,21 +1171,195 @@ there. (BUFFER cannot be the S process buffer.)" S-prev-load-dir/file S-source-modes nil)) - (comint-check-source filename) - (setq S-prev-load-dir/file - (cons (file-name-directory filename) - (file-name-nondirectory filename))) - (process-send-string "S" (format inferior-S-load-command - filename)) - (switch-to-S t)) - + (catch 'give-up ; In case we don't want to load after all + (let ((buff (get-file-buffer filename)) + tbuffer-p) + (if buff ; Buffer exists + (save-excursion + (set-buffer buff) + (setq tbuffer-p S-temp-buffer-p) + (if (buffer-modified-p buff) ; Buff exists and has changed + ;; save BUFF, but don't make a backup + ;; if we're about to delete it + (if tbuffer-p ; i.e. a result from a dump command + (save-buffer (if S-keep-dump-files 1 0)) + ;; Better check if it's just any old buffer + (if (y-or-n-p (format "Buffer %s modified. Save it? " + (buffer-name buff))) + (save-buffer) + ;; Maybe we should just give up here ... + (message + "Using current disk version (don't say I didn't warn you!)"))) + ;; Buffer hasn't changed lately, might need to write + ;; it back if the file is gone + (if tbuffer-p + (if (y-or-n-p + "Buffer hasn't changed. Really load it into S? ") + (if (file-exists-p (buffer-file-name buff)) nil + (set-buffer-modified-p t) ; so save will work + (save-buffer 0)) + (message "No load performed.") + (throw 'give-up nil)))))) + (setq S-prev-load-dir/file + (cons (file-name-directory filename) + (file-name-nondirectory filename))) + (let ((errbuffer (get-buffer-create S-error-buffer-name))) + (save-excursion + (set-buffer errbuffer) + (erase-buffer) + (S-command (format inferior-S-load-command filename) errbuffer) + (goto-char (point-max)) + (if (re-search-backward S-dump-error-re nil t) + (progn + (message "Errors: Use %s to find error." + (substitute-command-keys + "\\\\[S-parse-errors]")) + ;; This load failed, so set buffer as modified so the + ;; user will be warned if he tries to kill it + (if buff + (progn + (set-buffer buff) + (set-buffer-modified-p t)))) + (message "Load successful.") + (if (and tbuffer-p (not S-keep-dump-files)) + (delete-file filename))))))) + (S-switch-to-S t)) + +(defun S-parse-errors (showerr) + "Jump to error in last loaded S source file. +With prefix argument, only shows the errors S reported." + (interactive "P") + (let ((errbuff (get-buffer S-error-buffer-name))) + (if (not errbuff) + (error "You need to do a load first!") + (set-buffer errbuff) + (goto-char (point-max)) + (if + (re-search-backward + "^\\(Syntax error: .*\\) at line \\([0-9]*\\), file \\(.*\\)$" + nil + t) + (let* ((filename (buffer-substring (match-beginning 3) (match-end 3))) + (fbuffer (get-file-buffer filename)) + (linenum (string-to-int (buffer-substring (match-beginning 2) (match-end 2)))) + (errmess (buffer-substring (match-beginning 1) (match-end 1)))) + (if showerr + (display-buffer errbuff) + (if fbuffer nil + (setq fbuffer (find-file-noselect filename)) + (save-excursion + (set-buffer fbuffer) + (S-mode))) + (pop-to-buffer fbuffer) + (goto-line linenum)) + (princ errmess t)) + (message "Not a syntax error.") + (display-buffer errbuff))))) + + (defun S-search-path-tracker (str) - "This function monitors user input to the inferior S process so that -emacs can keep the S-search-list up to date. Completing-read uses this -list indirectly when it prompts for help or for an object to dump." + "Check if input STR changed the search path." +;;; This function monitors user input to the inferior S process so that +;;; emacs can keep the S-search-list up to date. Completing-read uses this +;;; list indirectly when it prompts for help or for an object to dump. (if (string-match S-change-sp-regexp str) (setq S-sp-change t))) +(defun S-cleanup () + "Delete all of S-mode's temporary buffers and files +(if S-keep-dump-files is nil) leaving you in the S process buffer. +Auto-save files and S help buffers are also deleted. Buffers whose +contents do not match with S's idea of the objects value *usually* +have the modified flag set, and you will be warned before such buffers +are killed. The exception to this is buffers which were saved in a +previous session before being loaded into S, and then read this +session. + +It's a good idea to run this before you quit. It is run automatically by +\\[S-quit]." + (interactive) + (if (yes-or-no-p "Delete all temporary files and buffers? ") + (progn + (mapcar '(lambda (buf) + (set-buffer buf) + (let ((fname (buffer-file-name buf)) + (asfnm buffer-auto-save-file-name)) + (if S-temp-buffer-p + (progn + (kill-buffer buf) + (if (or (buffer-name buf) + S-keep-dump-files) + ;; Don't do anything if buffer was not + ;; killed or dump files are kept + nil + ;; if the file exists, it stays! (consider + ;; dumping an object with an existing file) +;;; (if (and fname (file-exists-p fname)) +;;; (delete-file fname)) + ;; Auto-save files can go, since they're + ;; only associated with modified buffers + (if (and asfnm (file-exists-p asfnm)) + (delete-file asfnm))))))) + (buffer-list)) + (S-switch-to-S nil)))) + +(defun S-quit () + "Issue the q() command to S, and clean up." + (interactive) + (let ((sprocess (get-process "S"))) + (if (not sprocess) (error "No S process running.")) + (if (yes-or-no-p "Really quit from S? ") + (save-excursion + (S-cleanup) + (S-switch-to-S nil) + (goto-char (marker-position (process-mark sprocess))) + (insert "q()\n") + (process-send-string sprocess "q()\n"))))) + +(defun S-abort () + "Kill the S process, without executing .Last or terminating devices. +If you want to finish your session, use \\[S-quit] instead." +;;; Provided as a safety measure over the default binding of C-c C-z in +;;; comint-mode-map. + (interactive) + (ding) + (message "WARNING: q() will not be executed and graphics devices won't finish properly!") + (sit-for 5) + (if (yes-or-no-p "Still abort? ") + (comint-quit-subjob) + (message "Good move."))) + + + +;;; +;;; Tek terminal Graphics support +;;; + +(defun S-tek-mode-toggle nil + "Toggle S-tek-mode. +Resets the S-tek-simple-prompt when S-tek-mode is turned on." + (interactive) + (message (if (setq S-tek-mode (not S-tek-mode)) + "Tek mode is now ON." + "Tek mode is now OFF.")) + (if S-tek-mode (S-tek-get-simple-prompt))) + +(defun S-tek-get-simple-prompt nil + "Find the exact version of the current prompt." + (interactive) + (let ((tbuffer (generate-new-buffer "*S-exact-prompt*"))) + (buffer-flush-undo tbuffer) + (set-buffer tbuffer) + (S-command inferior-S-get-prompt-command tbuffer) + (goto-char (point-max)) + (re-search-backward "\"\\([^\"]*\\)\"" nil t) + (setq S-tek-simple-prompt + (buffer-substring (match-beginning 1) (match-end 1))) + (kill-buffer tbuffer))) + +;;; 25/6/92 dsmith +;;; Rest of code moved to S-tek.el + ;;; S mode @@ -596,21 +1370,32 @@ list indirectly when it prompts for help or for an object to dump." (if S-mode-map nil (setq S-mode-map (make-sparse-keymap)) - (define-key S-mode-map "\C-cr" 'S-eval-region) - (define-key S-mode-map "\C-c\C-r" 'S-eval-region-and-go) - (define-key S-mode-map "\C-cb" 'S-eval-buffer) - (define-key S-mode-map "\C-c\C-b" 'S-eval-buffer-and-go) - (define-key S-mode-map "\C-ce" 'S-eval-function) + (define-key S-mode-map "\C-c\C-r" 'S-eval-region) + (define-key S-mode-map "\C-c\M-r" 'S-eval-region-and-go) + (define-key S-mode-map "\C-c\C-b" 'S-eval-buffer) + (define-key S-mode-map "\C-c\M-b" 'S-eval-buffer-and-go) + (define-key S-mode-map "\C-c\C-f" 'S-eval-function) + (define-key S-mode-map "\C-c\M-f" 'S-eval-function-and-go) (define-key S-mode-map "\M-\C-x" 'S-eval-function) - (define-key S-mode-map "\C-c\C-e" 'S-eval-function-and-go) - (define-key S-mode-map "\C-ck" 'S-eval-line) - (define-key S-mode-map "\C-c\C-k" 'S-eval-line-and-go) - (define-key S-mode-map "\M-\C-a" 'beginning-of-S-function) - (define-key S-mode-map "\M-\C-e" 'end-of-S-function) - (define-key S-mode-map "\C-cz" 'switch-to-S) - (define-key S-mode-map "\C-c\C-z" 'switch-to-end-of-S) - (define-key S-mode-map "\C-cl" 'S-load-file) - (define-key S-mode-map "\C-ch" 'S-display-help-on-object)) + (define-key S-mode-map "\C-c\C-n" 'S-eval-line-and-next-line) + (define-key S-mode-map "\C-c\C-j" 'S-eval-line) + (define-key S-mode-map "\C-c\M-j" 'S-eval-line-and-go) + (define-key S-mode-map "\M-\C-a" 'S-beginning-of-function) + (define-key S-mode-map "\M-\C-e" 'S-end-of-function) + (define-key S-mode-map "\C-c\C-y" 'S-switch-to-S) + (define-key S-mode-map "\C-c\C-z" 'S-switch-to-end-of-S) + (define-key S-mode-map "\C-c\C-l" 'S-load-file) + (define-key S-mode-map "\C-c\C-h" 'S-display-help-on-object) + (define-key S-mode-map "\C-c\C-d" 'S-dump-object-into-edit-buffer) + (define-key S-mode-map "\C-c\C-e" 'S-execute-in-tb) + (define-key S-mode-map "\M-\t" 'S-complete-object-name) + (define-key S-mode-map "{" 'S-electric-brace) + (define-key S-mode-map "}" 'S-electric-brace) + (define-key S-mode-map "\e\C-h" 'S-mark-function) + (define-key S-mode-map "\e\C-q" 'S-indent-exp) + (define-key S-mode-map "\177" 'backward-delete-char-untabify) + (define-key S-mode-map "\t" 'S-indent-command) +) (defun S-mode () "Major mode for editing S source. @@ -625,10 +1410,10 @@ S source. S-eval-buffer sends the current buffer to the S process. S-eval-function sends the current function to the S process. S-eval-line sends the current line to the S process. - beginning-of-S-function and end-of-S-function move the point to + S-beginning-of-function and S-end-of-function move the point to the beginning and end of the current S function. - switch-to-S switches the current buffer to the S process buffer. - switch-to-end-of-S switches the current buffer to the S process + S-switch-to-S switches the current buffer to the S process buffer. + S-switch-to-end-of-S switches the current buffer to the S process buffer and puts point at the end of it. S-eval-region-and-go, S-eval-buffer-and-go, @@ -636,13 +1421,81 @@ S source. process buffer after sending their text. S-load-file sources a file of commands to the S process. - make-S-function inserts a function template in the buffer." - + S-make-function inserts a function template in the buffer. + +\\[S-indent-command] indents for S code. +\\[backward-delete-char-untabify] converts tabs to spaces as it moves back. +Comments are indented in a similar way to Emacs-lisp mode: + `###' beginning of line + `##' the same level of indentation as the code + `#' the same column on the right, or to the right of such a + column if that is not possible.(default value 40). + \\[indent-for-comment] command automatically inserts such a + `#' in the right place, or aligns such a comment if it is + already inserted. +\\[S-indent-exp] command indents each line of the S grouping following point. + +Variables controlling indentation style: + S-tab-always-indent + Non-nil means TAB in S mode should always reindent the current line, + regardless of where in the line point is when the TAB command is used. + S-auto-newline + Non-nil means automatically newline before and after braces inserted in S + code. + S-indent-level + Indentation of S statements within surrounding block. + The surrounding block's indentation is the indentation of the line on + which the open-brace appears. + S-continued-statement-offset + Extra indentation given to a substatement, such as the then-clause of an + if or body of a while. + S-continued-brace-offset + Extra indentation given to a brace that starts a substatement. + This is in addition to S-continued-statement-offset. + S-brace-offset + Extra indentation for line if it starts with an open brace. + S-arg-function-offset + Extra indent for internal substatements of function `foo' that called + in `arg=foo(...)' form. + If not number, the statements are indented at open-parenthesis following + `foo'. + S-expression-offset + Extra indent for internal substatements of `expression' that specified + in `obj <- expression(...)' form. + If not number, the statements are indented at open-parenthesis following + `expression'. + S-brace-imaginary-offset + An open brace following other text is treated as if it were + this far to the right of the start of its line. + +Furthermore, \\[S-set-style] command enables you to set up predefined S-mode +indentation style. At present, predefined style are `BSD', `GNU', `K&R' `C++' + (quoted from C language style)." (interactive) (setq major-mode 'S-mode) (setq mode-name "S") (use-local-map S-mode-map) (set-syntax-table S-mode-syntax-table) + (make-local-variable 'paragraph-start) + (setq paragraph-start (concat "^$\\|" page-delimiter)) + (make-local-variable 'paragraph-separate) + (setq paragraph-separate paragraph-start) + (make-local-variable 'paragraph-ignore-fill-prefix) + (setq paragraph-ignore-fill-prefix t) + (make-local-variable 'indent-line-function) + (setq indent-line-function 'S-indent-line) + (make-local-variable 'require-final-newline) + (setq require-final-newline t) + (make-local-variable 'comment-start) + (setq comment-start "#") + (make-local-variable 'comment-start-skip) + (setq comment-start-skip "#+ *") + (make-local-variable 'comment-column) + (setq comment-column 40) + (make-local-variable 'comment-indent-hook) + (setq comment-indent-hook 'S-comment-indent) + (make-local-variable 'parse-sexp-ignore-comments) + (setq parse-sexp-ignore-comments nil) (run-hooks 'S-mode-hook)) ;;; Emacs will set the mode for a file based on the file's header. @@ -653,126 +1506,863 @@ S source. ;;; s-mode in lower case too. That is, "#-*-S-*-" invokes s-mode and not S-mode. (fset 's-mode 'S-mode) -(defun S-eval-region (start end) - "Send the current region to the inferior S process." - (interactive "r") - (process-send-region "S" start end) - (process-send-string "S" "\n")) - -(defun S-eval-region-and-go (start end) - "Send the current region to the inferior S and switch to the process -buffer." - (interactive "r") - (S-eval-region start end) - (switch-to-S t)) - -(defun S-eval-buffer () - "Send the current buffer to the inferior S process." - (interactive) - (S-eval-region (point-min) (point-max))) +(defun S-eval-region (start end toggle &optional message) + "Send the current region to the inferior S process. +With prefix argument, toggle meaning of S-eval-visibly-p." + (interactive "r\nP") + (let ((visibly (if toggle (not S-eval-visibly-p) S-eval-visibly-p))) + (if visibly + (S-eval-visibly (buffer-substring start end)) + (S-eval-visibly (buffer-substring start end) + (or message "Eval region"))))) + +(defun S-eval-region-and-go (start end vis) + "Send the current region to the inferior S and switch to the process buffer. +Arg has same meaning as for S-eval-region." + (interactive "r\nP") + (S-eval-region start end vis) + (S-switch-to-S t)) + +(defun S-eval-buffer (vis) + "Send the current buffer to the inferior S process. +Arg has same meaning as for S-eval-region." + (interactive "P") + (S-eval-region (point-min) (point-max) vis "Eval buffer")) -(defun S-eval-buffer-and-go () - "Send the current buffer to the inferior S and switch to the process -buffer." +(defun S-eval-buffer-and-go (vis) + "Send the current buffer to the inferior S and switch to the process buffer. +Arg has same meaning as for S-eval-region." (interactive) - (S-eval-buffer) - (switch-to-S t)) + (S-eval-buffer vis) + (S-switch-to-S t)) -(defun S-eval-function () - "Send the current function to the inferior S process." - (interactive) +(defun S-eval-function (vis) + "Send the current function to the inferior S process. +Arg has same meaning as for S-eval-region." + (interactive "P") (save-excursion - (end-of-S-function) + (S-end-of-function) (let ((end (point))) - (beginning-of-S-function) - (message (concat "Loading: " (extract-word-name))) - (S-eval-region (point) end)))) + (S-beginning-of-function) + (princ (concat "Loading: " (S-extract-word-name)) t) + (S-eval-region (point) end vis + (concat "Eval function " (S-extract-word-name)))))) -(defun S-eval-function-and-go () +(defun S-eval-function-and-go (vis) "Send the current function to the inferior S process and switch to -the process buffer." - (interactive) - (S-eval-function) - (switch-to-S t)) +the process buffer. Arg has same meaning as for S-eval-region." + (interactive "P") + (S-eval-function vis) + (S-switch-to-S t)) -(defun S-eval-line () - "Send the current line to the inferior S process." - (interactive) +(defun S-eval-line (vis) + "Send the current line to the inferior S process. +Arg has same meaning as for S-eval-region." + (interactive "P") (save-excursion (end-of-line) (let ((end (point))) (beginning-of-line) - (message (concat "Loading line: " (extract-word-name) "...")) - (S-eval-region (point) end)))) + (princ (concat "Loading line: " (S-extract-word-name) " ...") t) + (S-eval-region (point) end vis "Eval line")))) -(defun S-eval-line-and-go () +(defun S-eval-line-and-go (vis) "Send the current line to the inferior S process and switch to the -process buffer." +process buffer. Arg has same meaning as for S-eval-region." + (interactive "P") + (S-eval-line vis) + (S-switch-to-S t)) + +(defun S-eval-line-and-next-line () + "Evaluate the current line visibly and move to the next line." + ;; From an idea by Rod Ball (rod@marcam.dsir.govt.nz) (interactive) - (S-eval-line) - (switch-to-S t)) + (save-excursion + (end-of-line) + (let ((end (point))) + (beginning-of-line) + (S-eval-visibly (buffer-substring (point) end)))) + (next-line 1)) -(defun beginning-of-S-function (&optional recurse) +(defun S-beginning-of-function nil "Leave the point at the beginning of the current S function." (interactive) - (let ((cp (point))) - (end-of-line) - (re-search-backward S-function-pattern (point-min) t) - (beginning-of-line) - (if (and (not (bobp)) - (not recurse) - (= (point) cp)) ;you are where you were, at the end - (progn (re-search-backward S-function-pattern (point-min) t) - (beginning-of-S-function t))))) - -(defun end-of-S-function (&optional recursive) + (let ((init-point (point)) + beg end done) + (if (search-forward "(" nil t) (forward-char 1)) + ;; in case we're sitting in a function header + (while (not done) + (if + (re-search-backward S-function-pattern (point-min) t) + nil + (goto-char init-point) + (error "Point is not in a function.")) + (setq beg (point)) + (forward-list 1) ; get over arguments + (forward-sexp 1) ; move over braces + (setq end (point)) + (goto-char beg) + ;; current function must begin and end around point + (setq done (and (>= end init-point) (<= beg init-point)))))) + +(defun S-end-of-function nil "Leave the point at the end of the current S function." (interactive) - (let ((cp (point))) - (beginning-of-S-function t) - (re-search-forward "{" (point-max) t) - (forward-char -1) - (forward-sexp) - (if (and (not (eobp)) - (not recursive) - (= (point) cp)) ;you are where you were, at the end - (progn (re-search-forward S-function-pattern (point-max) t) - (end-of-S-function t))))) - -(defun extract-word-name () + (S-beginning-of-function) + (forward-list 1) ; get over arguments + (forward-sexp 1) ; move over braces + ) + +(defun S-extract-word-name () "Get the word you're on." - (interactive) (save-excursion - (buffer-substring - (point) - ;; search until you get a non-word character - (progn (re-search-forward "[ <_=#-*&]") (point))))) + (re-search-forward "\\<\\w+\\>" nil t) + (buffer-substring (match-beginning 0) (match-end 0)))) -(defun switch-to-S (eob-p) +(defun S-switch-to-S (eob-p) "Switch to the inferior S process buffer. With argument, positions cursor at end of buffer." (interactive "P") (cond ((comint-check-proc "*S*") (pop-to-buffer "*S*") (cond (eob-p - (push-mark) (goto-char (point-max))))) (t (message "No inferior S process") (ding)))) -(defun switch-to-end-of-S (eob-p) +(defun S-switch-to-end-of-S nil "Switch to the end of the inferior S process buffer." - (interactive "P") - (switch-to-S t)) + (interactive) + (S-switch-to-S t)) -(defun make-S-function () +(defun S-make-function () "Insert a function template." (interactive) (insert "fu <- function()\n{\n\t\n}\n") (forward-line -2) (end-of-line)) +(defun S-complete-object-name (&optional listcomp) + ;;Based on lisp-complete-symbol + "Perform completion on S object preceding point. The object is +compared against those objects known by S-get-object-list and any +additional characters up to ambiguity are inserted. Completion only +works on globally-known objects (including elements of attached data +frames), and thus is most suitable for interactive command-line entry, +and not so much for function editing since local objects (e.g. +argument names) aren't known. + +Use \\[S-resynch] to re-read the names of the attached directories. +This is done automatically (and transparently) if a directory is +modified, so the most up-to-date list of object names is always +available. However attached dataframes are *not* updated, so this +command may be necessary if you modify an attached dataframe. + +If ARG is non-nil, no completion is attempted, but the available +completions are listed. + +If the character proceding point is not a symbol element, +indent-for-tab-command is run." + (interactive "P") + (if (memq (char-syntax (preceding-char)) '(?w ?_)) + (let* ((end (point)) + (buffer-syntax (syntax-table)) + (beg (unwind-protect + (save-excursion + (set-syntax-table S-mode-syntax-table) + (backward-sexp 1) + (point)) + (set-syntax-table buffer-syntax))) + (full-prefix (buffer-substring beg end)) + ;; See if we're indexing a list with `$' + (pattern full-prefix) + components + (listname (if (string-match "\\(.+\\)\\$\\(\\sw\\|\\s_\\)*$" + full-prefix) + (progn + (setq pattern + (if (not (match-beginning 2)) "" + (substring full-prefix + (match-beginning 2) + (match-end 2)))) + (substring full-prefix (match-beginning 1) + (match-end 1))))) + (completion (try-completion pattern + (if listname + (setq components + (S-object-names listname)) + (S-get-object-list))))) + (if listcomp (setq completion full-prefix)) + (cond ((eq completion t) + (message "[sole completion]")) + ((null completion) + (message "Can't find completion for \"%s\"" full-prefix) + (ding)) + ((not (string= pattern completion)) + (delete-region + (if listname (+ beg (length listname) 1) beg) + end) + (insert completion)) + (t + (message "Making completion list...") + (let ((list (all-completions pattern + (if listname components + (S-get-object-list))))) + (with-output-to-temp-buffer " *Completions*" + (display-completion-list list))) + (message "Making completion list...%s" "done")))) + (indent-for-tab-command))) + +;;; S code formatting functions + +(defun S-comment-indent () + (if (looking-at "###") + (current-column) + (if (looking-at "##") + (let ((tem (S-calculate-indent))) + (if (listp tem) (car tem) tem)) + (skip-chars-backward " \t") + (max (if (bolp) 0 (1+ (current-column))) + comment-column)))) + +(defun S-electric-brace (arg) + "Insert character and correct line's indentation." + (interactive "P") + (let (insertpos) + (if (and (not arg) + (eolp) + (or (save-excursion + (skip-chars-backward " \t") + (bolp)) + (if S-auto-newline (progn (S-indent-line) (newline) t) nil))) + (progn + (insert last-command-char) + (S-indent-line) + (if S-auto-newline + (progn + (newline) + ;; (newline) may have done auto-fill + (setq insertpos (- (point) 2)) + (S-indent-line))) + (save-excursion + (if insertpos (goto-char (1+ insertpos))) + (delete-char -1)))) + (if insertpos + (save-excursion + (goto-char insertpos) + (self-insert-command (prefix-numeric-value arg))) + (self-insert-command (prefix-numeric-value arg))))) + +(defun S-indent-command (&optional whole-exp) + "Indent current line as S code, or in some cases insert a tab character. +If S-tab-always-indent is non-nil (the default), always indent current line. +Otherwise, indent the current line only if point is at the left margin +or in the line's indentation; otherwise insert a tab. + +A numeric argument, regardless of its value, +means indent rigidly all the lines of the expression starting after point +so that this line becomes properly indented. +The relative indentation among the lines of the expression are preserved." + (interactive "P") + (if whole-exp + ;; If arg, always indent this line as S + ;; and shift remaining lines of expression the same amount. + (let ((shift-amt (S-indent-line)) + beg end) + (save-excursion + (if S-tab-always-indent + (beginning-of-line)) + (setq beg (point)) + (backward-up-list 1) + (forward-list 1) + (setq end (point)) + (goto-char beg) + (forward-line 1) + (setq beg (point))) + (if (> end beg) + (indent-code-rigidly beg end shift-amt))) + (if (and (not S-tab-always-indent) + (save-excursion + (skip-chars-backward " \t") + (not (bolp)))) + (insert-tab) + (S-indent-line)))) + +(defun S-indent-line () + "Indent current line as S code. +Return the amount the indentation changed by." + (let ((indent (S-calculate-indent nil)) + beg shift-amt + (case-fold-search nil) + (pos (- (point-max) (point)))) + (beginning-of-line) + (setq beg (point)) + (cond ((eq indent nil) + (setq indent (current-indentation))) + (t + (skip-chars-forward " \t") + (if (looking-at "###") + (setq indent 0)) + (if (and (looking-at "#") (not (looking-at "##"))) + (setq indent comment-column) + (if (eq indent t) (setq indent 0)) + (if (listp indent) (setq indent (car indent))) + (cond ((and (looking-at "else\\b") + (not (looking-at "else\\s_"))) + (setq indent (save-excursion + (S-backward-to-start-of-if) + (current-indentation)))) + ((= (following-char) ?}) + (setq indent (- indent S-indent-level))) + ((= (following-char) ?{) + (setq indent (+ indent S-brace-offset))))))) + (skip-chars-forward " \t") + (setq shift-amt (- indent (current-column))) + (if (zerop shift-amt) + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos))) + (delete-region beg (point)) + (indent-to indent) + ;; If initial point was within line's indentation, + ;; position after the indentation. + ;; Else stay at same point in text. + (if (> (- (point-max) pos) (point)) + (goto-char (- (point-max) pos)))) + shift-amt)) + +(defun S-calculate-indent (&optional parse-start) + "Return appropriate indentation for current line as S code. +In usual case returns an integer: the column to indent to. +Returns nil if line starts inside a string, t if in a comment." + (save-excursion + (beginning-of-line) + (let ((indent-point (point)) + (case-fold-search nil) + state + containing-sexp) + (if parse-start + (goto-char parse-start) + (beginning-of-defun)) + (while (< (point) indent-point) + (setq parse-start (point)) + (setq state (parse-partial-sexp (point) indent-point 0)) + (setq containing-sexp (car (cdr state)))) + (cond ((or (nth 3 state) (nth 4 state)) + ;; return nil or t if should not change this line + (nth 4 state)) + ((null containing-sexp) + ;; Line is at top level. May be data or function definition, + 0) ; Unless it starts a function body + ((/= (char-after containing-sexp) ?{) + ;; line is expression, not statement: + ;; indent to just after the surrounding open. + (goto-char containing-sexp) + (let ((bol (save-excursion (beginning-of-line) (point)))) + (cond ((and (numberp S-arg-function-offset) + (re-search-backward "=[ \t]*\\s\"*\\(\\w\\|\\s_\\)+\\s\"*[ \t]*" bol t)) + (forward-sexp -1) + (+ (current-column) S-arg-function-offset)) + ((and (numberp S-expression-offset) + (re-search-backward "<-[ \t]*expression[ \t]*" bol t)) + (forward-sexp -1) + (+ (current-column) S-expression-offset)) + (t + (progn (goto-char (1+ containing-sexp)) + (current-column)))))) + (t + ;; Statement level. Is it a continuation or a new statement? + ;; Find previous non-comment character. + (goto-char indent-point) + (S-backward-to-noncomment containing-sexp) + ;; Back up over label lines, since they don't + ;; affect whether our line is a continuation. + (while (eq (preceding-char) ?\,) + (S-backward-to-start-of-continued-exp containing-sexp) + (beginning-of-line) + (S-backward-to-noncomment containing-sexp)) + ;; Now we get the answer. + (if (S-continued-statement-p) + ;; This line is continuation of preceding line's statement; + ;; indent S-continued-statement-offset more than the + ;; previous line of the statement. + (progn + (S-backward-to-start-of-continued-exp containing-sexp) + (+ S-continued-statement-offset (current-column) + (if (save-excursion (goto-char indent-point) + (skip-chars-forward " \t") + (eq (following-char) ?{)) + S-continued-brace-offset 0))) + ;; This line starts a new statement. + ;; Position following last unclosed open. + (goto-char containing-sexp) + ;; Is line first statement after an open-brace? + (or + ;; If no, find that first statement and indent like it. + (save-excursion + (forward-char 1) + (while (progn (skip-chars-forward " \t\n") + (looking-at "#")) + ;; Skip over comments following openbrace. + (forward-line 1)) + ;; The first following code counts + ;; if it is before the line we want to indent. + (and (< (point) indent-point) + (current-column))) + ;; If no previous statement, + ;; indent it relative to line brace is on. + ;; For open brace in column zero, don't let statement + ;; start there too. If S-indent-level is zero, + ;; use S-brace-offset + S-continued-statement-offset instead. + ;; For open-braces not the first thing in a line, + ;; add in S-brace-imaginary-offset. + (+ (if (and (bolp) (zerop S-indent-level)) + (+ S-brace-offset S-continued-statement-offset) + S-indent-level) + ;; Move back over whitespace before the openbrace. + ;; If openbrace is not first nonwhite thing on the line, + ;; add the S-brace-imaginary-offset. + (progn (skip-chars-backward " \t") + (if (bolp) 0 S-brace-imaginary-offset)) + ;; If the openbrace is preceded by a parenthesized exp, + ;; move to the beginning of that; + ;; possibly a different line + (progn + (if (eq (preceding-char) ?\)) + (forward-sexp -1)) + ;; Get initial indentation of the line we are on. + (current-indentation)))))))))) + +(defun S-continued-statement-p () + (let ((eol (point))) + (save-excursion + (cond ((memq (preceding-char) '(nil ?\, ?\; ?\} ?\{ ?\])) + nil) + ((bolp)) + ((= (preceding-char) ?\)) + (forward-sexp -2) + (looking-at "if\\b[ \t]*(\\|function\\b[ \t]*(\\|for\\b[ \t]*(\\|while\\b[ \t]*(")) + ((progn (forward-sexp -1) + (and (looking-at "else\\b\\|repeat\\b") + (not (looking-at "else\\s_\\|repeat\\s_")))) + (skip-chars-backward " \t") + (or (bolp) + (= (preceding-char) ?\;))) + (t + (progn (goto-char eol) + (skip-chars-backward " \t") + (or (and (> (current-column) 1) + (save-excursion (backward-char 1) + (looking-at "[-:+*/_><=]"))) + (and (> (current-column) 3) + (progn (backward-char 3) + (looking-at "%[^ \t]%")))))))))) + +(defun S-backward-to-noncomment (lim) + (let (opoint stop) + (while (not stop) + (skip-chars-backward " \t\n\f" lim) + (setq opoint (point)) + (beginning-of-line) + (skip-chars-forward " \t") + (setq stop (or (not (looking-at "#")) (<= (point) lim))) + (if stop (goto-char opoint) + (beginning-of-line))))) + +(defun S-backward-to-start-of-continued-exp (lim) + (if (= (preceding-char) ?\)) + (forward-sexp -1)) + (beginning-of-line) + (if (<= (point) lim) + (goto-char (1+ lim))) + (skip-chars-forward " \t")) + +(defun S-backward-to-start-of-if (&optional limit) + "Move to the start of the last ``unbalanced'' if." + (or limit (setq limit (save-excursion (beginning-of-defun) (point)))) + (let ((if-level 1) + (case-fold-search nil)) + (while (not (zerop if-level)) + (backward-sexp 1) + (cond ((looking-at "else\\b") + (setq if-level (1+ if-level))) + ((looking-at "if\\b") + (setq if-level (1- if-level))) + ((< (point) limit) + (setq if-level 0) + (goto-char limit)))))) + +(defun S-mark-function () + "Put mark at end of S function, point at beginning." + (interactive) + (push-mark (point)) + (S-end-of-function) + (push-mark (point)) + (S-beginning-of-function)) + +(defun S-indent-exp () + "Indent each line of the S grouping following point." + (interactive) + (let ((indent-stack (list nil)) + (contain-stack (list (point))) + (case-fold-search nil) + restart outer-loop-done inner-loop-done state ostate + this-indent last-sexp + at-else at-brace + (opoint (point)) + (next-depth 0)) + (save-excursion + (forward-sexp 1)) + (save-excursion + (setq outer-loop-done nil) + (while (and (not (eobp)) (not outer-loop-done)) + (setq last-depth next-depth) + ;; Compute how depth changes over this line + ;; plus enough other lines to get to one that + ;; does not end inside a comment or string. + ;; Meanwhile, do appropriate indentation on comment lines. + (setq innerloop-done nil) + (while (and (not innerloop-done) + (not (and (eobp) (setq outer-loop-done t)))) + (setq ostate state) + (setq state (parse-partial-sexp (point) (progn (end-of-line) (point)) + nil nil state)) + (setq next-depth (car state)) + (if (and (car (cdr (cdr state))) + (>= (car (cdr (cdr state))) 0)) + (setq last-sexp (car (cdr (cdr state))))) + (if (or (nth 4 ostate)) + (S-indent-line)) + (if (nth 4 state) + (and (S-indent-line) + (setcar (nthcdr 4 state) nil))) + (if (or (nth 3 state)) + (forward-line 1) + (setq innerloop-done t))) + (if (<= next-depth 0) + (setq outer-loop-done t)) + (if outer-loop-done + nil + ;; If this line had ..))) (((.. in it, pop out of the levels + ;; that ended anywhere in this line, even if the final depth + ;; doesn't indicate that they ended. + (while (> last-depth (nth 6 state)) + (setq indent-stack (cdr indent-stack) + contain-stack (cdr contain-stack) + last-depth (1- last-depth))) + (if (/= last-depth next-depth) + (setq last-sexp nil)) + ;; Add levels for any parens that were started in this line. + (while (< last-depth next-depth) + (setq indent-stack (cons nil indent-stack) + contain-stack (cons nil contain-stack) + last-depth (1+ last-depth))) + (if (null (car contain-stack)) + (setcar contain-stack (or (car (cdr state)) + (save-excursion (forward-sexp -1) + (point))))) + (forward-line 1) + (skip-chars-forward " \t") + (if (eolp) + nil + (if (and (car indent-stack) + (>= (car indent-stack) 0)) + ;; Line is on an existing nesting level. + ;; Lines inside parens are handled specially. + (if (/= (char-after (car contain-stack)) ?{) + (setq this-indent (car indent-stack)) + ;; Line is at statement level. + ;; Is it a new statement? Is it an else? + ;; Find last non-comment character before this line + (save-excursion + (setq at-else (looking-at "else\\W")) + (setq at-brace (= (following-char) ?{)) + (S-backward-to-noncomment opoint) + (if (S-continued-statement-p) + ;; Preceding line did not end in comma or semi; + ;; indent this line S-continued-statement-offset + ;; more than previous. + (progn + (S-backward-to-start-of-continued-exp (car contain-stack)) + (setq this-indent + (+ S-continued-statement-offset (current-column) + (if at-brace S-continued-brace-offset 0)))) + ;; Preceding line ended in comma or semi; + ;; use the standard indent for this level. + (if at-else + (progn (S-backward-to-start-of-if opoint) + (setq this-indent (current-indentation))) + (setq this-indent (car indent-stack)))))) + ;; Just started a new nesting level. + ;; Compute the standard indent for this level. + (let ((val (S-calculate-indent + (if (car indent-stack) + (- (car indent-stack)))))) + (setcar indent-stack + (setq this-indent val)))) + ;; Adjust line indentation according to its contents + (if (= (following-char) ?}) + (setq this-indent (- this-indent S-indent-level))) + (if (= (following-char) ?{) + (setq this-indent (+ this-indent S-brace-offset))) + ;; Put chosen indentation into effect. + (or (= (current-column) this-indent) + (= (following-char) ?\#) + (progn + (delete-region (point) (progn (beginning-of-line) (point))) + (indent-to this-indent))) + ;; Indent any comment following the text. + (or (looking-at comment-start-skip) + (if (re-search-forward comment-start-skip (save-excursion (end-of-line) (point)) t) + (progn (indent-for-comment) (beginning-of-line))))))))) +; (message "Indenting S expression...done") + ) + +;; Predefined styles +(defun S-set-style (&optional style) + "Set up the S-mode style variables from the S-style variable or if + STYLE argument is given, use that. It makes the S indentation style + variables buffer local." + + (interactive) + + (let ((S-styles (mapcar 'car S-style-alist))) + + (if (interactive-p) + (setq style + (let ((style-string ; get style name with completion + (completing-read + (format "Set S mode indentation style to (default %s): " + S-default-style) + (vconcat S-styles) + (function (lambda (arg) (memq arg S-styles))) + ))) + (if (string-equal "" style-string) + S-default-style + (intern style-string)) + ))) + + (setq style (or style S-style)) ; use S-style if style is nil + + (make-local-variable 'S-style) + (if (memq style S-styles) + (setq S-style style) + (error (concat "Bad S style: " style)) + ) + (message "S-style: %s" S-style) + + ; finally, set the indentation style variables making each one local + (mapcar (function (lambda (S-style-pair) + (make-local-variable (car S-style-pair)) + (set (car S-style-pair) + (cdr S-style-pair)))) + (cdr (assq S-style S-style-alist))) + S-style)) + + + +;;; S-help-mode +;;;====================================================== +;;; + +(defvar S-help-mode-map nil "Keymap for S help mode.") +(defvar S-help-mode-hook nil "Functions to call when entering more mode. ") + +(defvar S-help-sec-map nil "Sub-keymap for S help mode.") + +(defun S-skip-to-help-section nil + "Jump to a section heading of a help buffer. The section selected is +determined by the command letter used to invoke the command, as indicated +by S-help-sec-keys-alist. Use \\[S-describe-sec-map] to see which keystrokes +find which sections." + (interactive) + (let ((old-point (point))) + (goto-char (point-min)) + (let ((the-sec (cdr (assoc last-command-char S-help-sec-keys-alist)))) + (if (not the-sec) (error "Invalid section key: %c" last-command-char) + (if (re-search-forward (concat "^" the-sec) nil t) nil + (message "No %s section in this help. Sorry." the-sec) + (goto-char old-point)))))) + +(defun S-skip-to-next-section nil + "Jump to next section in S help buffer." + (interactive) + (let ((case-fold-search nil)) + (if (re-search-forward "^[A-Z. ---]+:$" nil t) nil + (message "No more sections.")))) + +(defun S-skip-to-previous-section nil + "Jump to previous section in S help buffer." + (interactive) + (let ((case-fold-search nil)) + (if (re-search-backward "^[A-Z. ---]+:$" nil t) nil + (message "No previous section.")))) + +(defun S-describe-help-mode nil +"Display help for S-mode" + (interactive) + (describe-function 'S-help-mode)) + +(defun S-kill-buffer-and-go nil + "Kill the current buffer and switch back to S" + (interactive) + (kill-buffer (current-buffer)) + (S-switch-to-S nil)) + +(defun S-describe-sec-map nil + "Display help for the `s' key." + (interactive) + (describe-function 'S-skip-to-help-section) + (save-excursion + (set-buffer "*Help*") + (goto-char (point-max)) + (insert "\n\nCurrently defined keys are: + +Keystroke Section +--------- -------\n") + (mapcar '(lambda (cs) (insert " " (car cs) " " (cdr cs) "\n")) S-help-sec-keys-alist) + (insert "\nFull list of key definitions:\n" (substitute-command-keys "\\{S-help-sec-map}")))) + +(defun S-find-help-file (p-string) + (let* ((default (S-read-object-name-default)) + (prompt-string (if default + (format "%s(default %s) " p-string default) + p-string)) + (help-files-list (S-get-help-files-list)) + (spec (completing-read prompt-string help-files-list))) + (list (cond + ((string= spec "") default) + (t spec))))) + +(defun S-get-help-files-list nil + (mapcar 'list + (apply 'append + (mapcar '(lambda (dirname) + (if (file-directory-p dirname) + (directory-files dirname))) + (mapcar '(lambda (str) (concat str "/.Help")) + S-search-list))))) + +(if S-help-sec-map + nil + (setq S-help-sec-map (make-keymap)) + (mapcar '(lambda (key) + (define-key S-help-sec-map (char-to-string key) + 'S-skip-to-help-section)) + (mapcar 'car S-help-sec-keys-alist)) + (define-key S-help-sec-map "?" 'S-describe-sec-map) + (define-key S-help-sec-map ">" 'end-of-buffer) + (define-key S-help-sec-map "<" 'beginning-of-buffer) +) + +(defun S-display-help-on-object (object) + "Display the help page for OBJECT in the *Help* buffer. +If prefix arg is given, forces a query of the S process for the help +file. Otherwise just pops to an existing buffer if it exists." + (interactive (S-find-help-file "Help on: ")) + (let* ((hb-name (concat "*help(" object ")*")) + (old-hb-p (get-buffer hb-name)) + (tbuffer (get-buffer-create hb-name))) + (set-buffer tbuffer) + (if (or (not old-hb-p) current-prefix-arg) + ;; Ask S for the help file + (progn + (setq S-temp-buffer-p t) ; Flag as a temp buffer + (delete-region (point-min) (point-max)) + (S-help-mode) + (S-command (format inferior-S-help-command object) tbuffer) + (S-nuke-help-bs) + (goto-char (point-min)))) + (let (nodocs) + (save-excursion + (goto-char (point-min)) + (setq nodocs + (re-search-forward "\\`No documentation available.*$" nil t)) + (if nodocs + (progn + (princ (buffer-substring (match-beginning 0) (match-end 0)) t) + ;; Avoid using 'message here -- may be %'s in string + (ding) + (kill-buffer tbuffer)) + (if (eq major-mode 'S-help-mode) (switch-to-buffer tbuffer) + (pop-to-buffer tbuffer))))))) + +;;; This function is a modification of nuke-nroff-bs in man.el from the +;;; standard emacs 18 lisp library. +(defun S-nuke-help-bs () + (interactive "*") + ;; Nuke underlining and overstriking (only by the same letter) + (goto-char (point-min)) + (while (search-forward "\b" nil t) + (let* ((preceding (char-after (- (point) 2))) + (following (following-char))) + (cond ((= preceding following) + ;; x\bx + (delete-char -2)) + ((= preceding ?\_) + ;; _\b + (delete-char -2)) + ((= following ?\_) + ;; \b_ + (delete-region (1- (point)) (1+ (point))))))) + ;; Crunch blank lines + (goto-char (point-min)) + (while (re-search-forward "\n\n\n\n*" nil t) + (replace-match "\n\n")) + ;; Nuke blanks lines at start. + (goto-char (point-min)) + (skip-chars-forward "\n") + (delete-region (point-min) (point))) + +(if S-help-mode-map + nil + (setq S-help-mode-map (make-keymap)) + (suppress-keymap S-help-mode-map) + (define-key S-help-mode-map " " 'scroll-up) + (define-key S-help-mode-map "b" 'scroll-down) + (define-key S-help-mode-map "q" 'S-switch-to-end-of-S) + (define-key S-help-mode-map "\177" 'scroll-down) ; DEL + (define-key S-help-mode-map "s" S-help-sec-map) + (define-key S-help-mode-map "h" 'S-display-help-on-object) + (define-key S-help-mode-map "r" 'S-eval-region) + (define-key S-help-mode-map "n" 'S-skip-to-next-section) + (define-key S-help-mode-map "p" 'S-skip-to-previous-section) + (define-key S-help-mode-map "/" 'isearch-forward) + (define-key S-help-mode-map ">" 'end-of-buffer) + (define-key S-help-mode-map "<" 'beginning-of-buffer) + (define-key S-help-mode-map "x" 'S-kill-buffer-and-go) + (define-key S-help-mode-map "?" 'S-describe-help-mode) + (define-key S-help-mode-map "\C-c\C-r" 'S-eval-region) + (define-key S-help-mode-map "\C-c\M-r" 'S-eval-region-and-go) + (define-key S-help-mode-map "\C-c\C-f" 'S-eval-function) + (define-key S-help-mode-map "\M-\C-x" 'S-eval-function) + (define-key S-help-mode-map "\C-c\M-f" 'S-eval-function-and-go) + (define-key S-help-mode-map "\C-c\C-j" 'S-eval-line) + (define-key S-help-mode-map "\C-c\M-j" 'S-eval-line-and-go) + (define-key S-help-mode-map "\M-\C-a" 'S-beginning-of-function) + (define-key S-help-mode-map "\M-\C-e" 'S-end-of-function) + (define-key S-help-mode-map "\C-c\C-y" 'S-switch-to-S) + (define-key S-help-mode-map "\C-c\C-z" 'S-switch-to-end-of-S) + (define-key S-help-mode-map "\C-c\C-l" 'S-load-file) + (define-key S-help-mode-map "\C-c\C-h" 'S-display-help-on-object)) + +;;; Largely ripped from more-mode.el, +;;; by Wolfgang Rupprecht wolfgang@mgm.mit.edu + +(defun S-help-mode () + "Mode for viewing S help files. +Use SPC and DEL to page back and forth through the file. +Use `s' to jump to a particular section; `s ?' for help. +Use `q' to return to your S session; `x' to kill this buffer first. +The usual commands for evaluating S source are available. +Other keybindings are as follows: +\\{S-help-mode-map}" + (interactive) + (setq major-mode 'S-help-mode) + (setq mode-name "S Help") + (use-local-map S-help-mode-map) + (run-hooks S-help-mode-hook)) + (run-hooks 'S-mode-load-hook) @@ -786,11 +2376,11 @@ With argument, positions cursor at end of buffer." ;; * added S-mode-load-hook & S-pre-run-hook ;; and testing by neilc@research.att.com ;; Jul 9 1991 Frank Ritter -;; * Changed command-to-S to use a register rather than +;; * Changed S-command to use a register rather than ;; the kill ring. ;; * Better file header, comments now at 60 col so ;; mailers wont' eat them. -;; * Better extract-word-name. +;; * Better S-extract-word-name. ;; * Added S-mode-version variable ;; * Changed syntax table to read |#; appropriately ;; @@ -808,9 +2398,53 @@ With argument, positions cursor at end of buffer." ;; run from that directory. ;; ;; Thu Oct 18 12:41:52 1990 Ed Kademan (kademan at hermes) -;; * Added function nuke-S-help-bs to clean up nroff +;; * Added function S-nuke-help-bs to clean up nroff ;; style text in the S help buffer. This function is ;; a modification of nuke-nroff-bs from man.el. ;; ------------------------------------------------------- ;; Unnumbered version released dated Thu Jun 14 09:56:56 CDT 1990 +;; +;; Fri Jan 17 1992 Dave Smith (dsmith@stats.adelaide.edu.au) +;; * Help mode for reading files. When asking for an object to +;; run help on, completion is over those help files that exist. +;; * Added object name completion, and made S-get-object-list +;; efficient enough to make it worthwile. +;; * Error parsing for loaded files +;; * Better customization of file-names, with sensible defaults +;; * Sensible buffer names for object buffers +;; * Corrected definition for `.' in syntax table +;; * Improved (and simplified) S-read-object-name-default +;; * Included pager='cat' to default help-command specification +;; * Added a call to run-hook for S-pre-run-hook +;; * Changed keymaps to conform with GNU guidelines +;; (i.e. no \C-letter bindings) +;; * S-command has a new third argument, visible +;; +;; Tue May 27 1992 Dave Smith (dsmith@stats.adelaide.edu.au) +;; * now copes with dynamically changing prompts (reported by Doug Bates) +;; +;; Thu May 29 1992 Dave Smith (dsmith@stats.adelaide.edu.au) +;; * Added S-execute, modified S-execute-* to use it. +;; +;; Mon Jun 22 1992 dsmith +;; * Added S-mode editing commands written by Ken'ichi Shibayama +;; (shiba@isac.co.jp). A big win. +;; * Removed the redundant argument to S-switch-to-end-of-S +;; * S-function-pattern improved +;; * added S-eval-visibly, S-eval-visibly-p and modified S-eval-* +;; to use them +;; * added S-eval-line-and-next-line +;; * eval commands can now echo in the process buffer +;; * added S-kill-output and S-view-at-bottom +;; * added a binding for comint-isearch and autoloaded it +;; * added S-execute-in-tb. S-parse-errors now takes prefix arg. +;; +;; Thu Jun 25 1992 dsmith +;; * Moved some doctrings to comments (Frank Ritter) +;; * The Tek stuff now lives in a separate file (Frank Ritter) +;; * Fiddly C-c ESC M-. bindings in S mode and Help mode moved +;; to C-c M-. bindings (Martin Maechler) +;; * S-execute-objects now uses variable inferior-S-objects-command +;; whose value depends on S version. (Ken'ichi Shibayama) +;; * Symbols given uniform prefixes: S- or inferior-S- (Frank Ritter) diff --git a/comint-extra.el b/comint-extra.el new file mode 100644 index 000000000..0888d486a --- /dev/null +++ b/comint-extra.el @@ -0,0 +1,74 @@ +;;; -*-Emacs-Lisp-*- General command interpreter in a window stuff +;;; Copyright Olin Shivers (1988). +;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright +;;; notice appearing here to the effect that you may use this code any +;;; way you like, as long as you don't charge money for it, remove this +;;; notice, or hold me liable for its results. + +;;; The changelog is at the end of this file. + +;;; Please send me bug reports, bug fixes, and extensions, so that I can +;;; merge them into the master source. +;;; - Olin Shivers (shivers@cs.cmu.edu) + +;;; These are the less-commonly-used functions from comint.el + +(require 'comint) +(provide 'comint-extra) + +(defun comint-psearch-input () + "Search forwards for next occurrence of prompt and skip to end of line. +\(prompt is anything matching regexp comint-prompt-regexp)" + (interactive) + (if (re-search-forward comint-prompt-regexp (point-max) t) + (end-of-line) + (error "No occurrence of prompt found"))) + +(defun comint-msearch-input () + "Search backwards for previous occurrence of prompt and skip to end of line. +Search starts from beginning of current line." + (interactive) + (let ((p (save-excursion + (beginning-of-line) + (cond ((re-search-backward comint-prompt-regexp (point-min) t) + (end-of-line) + (point)) + (t nil))))) + (if p (goto-char p) + (error "No occurrence of prompt found")))) + +(defun comint-msearch-input-matching (str) + "Search backwards for occurrence of prompt followed by STRING. +STRING is prompted for, and is NOT a regular expression." + (interactive (let ((s (read-from-minibuffer + (format "Command (default %s): " + comint-last-input-match)))) + (list (if (string= s "") comint-last-input-match s)))) +; (interactive "sCommand: ") + (setq comint-last-input-match str) ; update default + (let* ((r (concat comint-prompt-regexp (regexp-quote str))) + (p (save-excursion + (beginning-of-line) + (cond ((re-search-backward r (point-min) t) + (end-of-line) + (point)) + (t nil))))) + (if p (goto-char p) + (error "No match")))) + +(defun comint-next-similar-input (arg) + "Reenters the next input that matches the string typed so far. If repeated +successively newer inputs are reentered. If arg is -1, it will go back +in the history, if 1 it will go forward." + (interactive "p") + (setq this-command 'comint-previous-similar-input) + (comint-previous-similar-input (- arg))) + +;;; Change log: +;;; 31/3/92 Dave Smith dsmith@stats.adelaude.edu.au +;;; - Created this file. It currently comprises the deprecated functions +;;; from version 2.03 of comint.el which I happened to like. It is +;;; currently the user's job to load this file when required, but maybe +;;; comint.el could handle it with autoloads. +;;; - Added comint-next-similar-input, ideal for binding to an arrow key. + diff --git a/comint-isearch.el b/comint-isearch.el new file mode 100644 index 000000000..379ad9621 --- /dev/null +++ b/comint-isearch.el @@ -0,0 +1,291 @@ +;;; -*-Emacs-Lisp-*- Incremental command search for comint +;;; Terry Glanfield (tg.southern@rxuk.xerox.com) +;;; Version 1.0 +;;; +;;; This file is not part of GNU Emacs but the same permissions apply. +;;; +;;; GNU Emacs is free software; you can redistribute it and/or modify +;;; it under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 1, or (at your option) +;;; any later version. +;;; +;;; GNU Emacs is distributed in the hope that it will be useful, +;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Emacs; see the file COPYING. If not, write to +;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;;; +;;; +;;; This is an incremental command search mode for comint. Previous +;;; commands that match are inserted on the command line as you type. +;;; Anyone familiar with the emacs isearch or bash should find this easy +;;; to adapt to. +;;; +;;; It is based on ideas from bash and borrows heavily from isearch.el. +;;; +;;; Differences from bash: +;;; * C-s searches forwards +;;; * DEL cancels characters or moves to previous search +;;; * C-g also moves to previous successful search +;;; * Inserts previous search string +;;; +;;; Differences from isearch: +;;; * No C-w or C-y +;;; * No wrap around, yet +;;; +;;; It uses the variables search-repeat-char, search-exit-char etc. +;;; +;;; recommended usage: +;;; (setq cmushell-load-hook +;;; '((lambda () +;;; (require 'comint-isearch) +;;; (define-key cmushell-mode-map "\C-r" 'comint-isearch)))) + +;; LCD Archive Entry: +;; comint-isearch|Terry Glanfield|tg.southern@rxuk.xerox.com +;; |Command line incremental searching for comint. +;; |92-04-10|Version 1.0|~/packages/comint-isearch.el.Z + +(require 'comint) +(provide 'comint-isearch) + +(defvar comint-last-isearch-string nil + "Last string searched for in comint-isearch.") + +(defvar comint-isearch-buffer " *Ring Buffer*" + "Buffer used in comint-isearch") + +(defun comint-isearch () + "Do incremental searching for commands in comint shells. +As you type characters, they add to the search string and the +matching command line from the history ring is inserted. +Type Delete to cancel characters from end of search string. +Type ESC to exit, leaving point at location found. +Type C-r to search again, C-s to search again forwards. +Type C-q to quote control character to search for it. +Type RET to send this command to the shell. +Other control and meta characters terminate the search + and are then executed normally. +The above special characters are mostly controlled by parameters; + do M-x apropos on search-.*-char to find them. +C-g while searching or when search has failed + cancels input back to what has been found successfully. +C-g when search is successful aborts and moves point to starting point." + (interactive) + (let ((ring-len (ring-length input-ring))) + (cond ((not (comint-after-pmark-p)) + (isearch-backward)) + ((<= ring-len 0) + (message "Empty input ring") + (ding)) + (t + (comint-isearch-internal))))) + +(defun comint-isearch-internal () + (let* ((search-string "") + (search-message "") + (success t) + (cmds nil) + (forward nil) + (pmark (marker-position + (process-mark (get-buffer-process (current-buffer))))) + (saved-prompt (buffer-substring + (save-excursion (beginning-of-line) (point)) + pmark)) + (saved-command (buffer-substring + pmark + (save-excursion (end-of-line) (point)))) + (saved-point (- (point-max) (point))) + (ring input-ring) + (ring-buf (get-buffer-create comint-isearch-buffer)) + (ring-point nil) + (line-point 0) + (abort-flag nil) + (inhibit-quit t)) ;Prevent ^G from quitting. + (save-excursion + (set-buffer ring-buf) + ;; fill temporary buffer with history ring + (erase-buffer) + (let ((n ring-len)) + (while (> n 0) + (setq n (1- n)) + (insert (ring-ref ring n) 10))) + (insert saved-command) + (end-of-buffer) + (backward-char saved-point) + (setq ring-point (point)) + (setq line-point (- (point-max) (point)))) + (comint-isearch-push) + (catch 'search-done + (while t + (or (>= unread-command-char 0) + (progn + (or (input-pending-p) + (comint-isearch-prompt)))) + (let ((char (if quit-flag + ?\C-g + (read-char)))) + (setq quit-flag nil) + (cond ((and (>= char 128) + search-exit-option) + (setq unread-command-char char) + ;; Meta character means exit search. + (setq unread-command-char char) + (throw 'search-done t)) + ((eq char search-exit-char) + ;; Esc means exit search normally. + (throw 'search-done t)) + ((= char ?\C-g) + ;; ^G means the user tried to quit. + ;; needs to be more clever + (ding) + (discard-input) + (if success + ;; really do quit. + (progn (setq abort-flag t) + (throw 'search-done t)) + ;; If search is failing, rub out until it is once more + ;; successful. + (while (not success) (comint-isearch-pop)) + ;; If it is now at the start, exit anyway + (if (equal search-string "") + (progn (setq abort-flag t) + (throw 'search-done t))))) + ((or (eq char search-repeat-char) + (eq char search-reverse-char)) + (if (eq forward (eq char search-repeat-char)) + ;; C-s in forward or C-r in reverse. + (if (equal search-string "") + ;; If search string is empty, use last one. + (setq search-string comint-last-isearch-string + search-message (mapconcat 'text-char-description + search-string "")) + ;; If already have what to search for, repeat it. + (or success (ding))) + ;; C-s in reverse or C-r in forward, change direction. + (setq forward (not forward))) + (setq success t) + (or (equal search-string "") + (comint-isearch-search t)) + (comint-isearch-push)) + ((= char search-delete-char) + ;; Rubout means discard last input item and move point + ;; back. If buffer is empty, just beep. + (if (null (cdr cmds)) + (ding) + (comint-isearch-pop))) + ((or (= char ?\r) + (= char ?\n)) + ;; Accept this line + (setq unread-command-char char) + (throw 'search-done t)) + (t + ;; could add search-yank-word-char + ;; and search-yank-line-char in here + (cond ((and + search-exit-option + (/= char search-quote-char) + (or (= char ?\177) + (and (< char ? ) (/= char ?\t) (/= char ?\r)))) + ;; Any other control char => + ;; unread it and exit the search normally. + (setq unread-command-char char) + (throw 'search-done t)) + (t + ;; Any other character => add it to the + ;; search string and search. + (and (= char search-quote-char) + (setq char (read-quoted-char + (comint-isearch-prompt t)))) + (setq search-string (concat + search-string + (char-to-string char)) + search-message (concat + search-message + (text-char-description char))))) + (if success + (comint-isearch-search)) + (comint-isearch-push)))))) + (message "") + (delete-region + (progn (beginning-of-line) (point)) + (progn (end-of-line) (point))) + (insert-string saved-prompt) + (if (or abort-flag + (equal search-string "")) + (progn (insert saved-command) + (backward-char saved-point)) + (progn (insert (comint-isearch-selected-line)) + (backward-char line-point) + (if (> (length search-string) 0) + (setq comint-last-isearch-string search-string)))) + (set-marker (process-mark (get-buffer-process (current-buffer))) pmark))) + +(defun comint-isearch-selected-line () + (save-excursion + (set-buffer ring-buf) + (goto-char ring-point) + (beginning-of-line) + (buffer-substring + (point) + (progn (end-of-line) (point))))) + +(defun comint-isearch-prompt (&optional c-q-hack) + (let ((m (concat "(I-search: '" + search-message + (if c-q-hack "^Q" "") + "'): ")) + (c (if ring-point + (comint-isearch-selected-line) + ""))) + (beginning-of-line) + (delete-region (point) (save-excursion (end-of-line) (point))) + (insert-string m c) + (backward-char line-point) + ;; ditch garbage from mini-buffer + (message " "))) + +(defun comint-isearch-pop () + (setq cmds (cdr cmds)) + (let ((cmd (car cmds))) + (setq search-string (car cmd) + search-message (car (cdr cmd)) + success (nth 2 cmd) + forward (nth 3 cmd) + ring-point (nth 4 cmd) + line-point (nth 5 cmd)) + (save-excursion + (set-buffer ring-buf) + (goto-char ring-point)))) + +(defun comint-isearch-push () + (setq cmds (cons (list search-string search-message success + forward ring-point line-point) + cmds))) + +(defun comint-isearch-search (&optional repeat) + (save-excursion + (set-buffer ring-buf) + (if forward + (goto-char ring-point) + (goto-char (+ ring-point (if (null repeat) (length search-string) 0)))) + (condition-case lossage + (let ((inhibit-quit nil)) + (setq success + (funcall + (if forward 'search-forward 'search-backward) + search-string nil t)) + (if success + (progn (setq ring-point (point)) + (end-of-line) + (setq line-point (- (point) ring-point))))) + (quit (setq unread-command-char ?\C-g) + (setq success nil))) + (if success + nil + ;; Ding if failed this time after succeeding last time. + (and (nth 2 (car cmds)) + (ding))))) diff --git a/comint.el b/comint.el new file mode 100644 index 000000000..575952778 --- /dev/null +++ b/comint.el @@ -0,0 +1,1386 @@ +;;; -*-Emacs-Lisp-*- General command interpreter in a window stuff +;;; Copyright Olin Shivers (1988). +;;; Please imagine a long, tedious, legalistic 5-page gnu-style copyright +;;; notice appearing here to the effect that you may use this code any +;;; way you like, as long as you don't charge money for it, remove this +;;; notice, or hold me liable for its results. + +;;; The changelog is at the end of this file. + +;;; Please send me bug reports, bug fixes, and extensions, so that I can +;;; merge them into the master source. +;;; - Olin Shivers (shivers@cs.cmu.edu) + +;;; This hopefully generalises shell mode, lisp mode, tea mode, soar mode,... +;;; This file defines a general command-interpreter-in-a-buffer package +;;; (comint mode). The idea is that you can build specific process-in-a-buffer +;;; modes on top of comint mode -- e.g., lisp, shell, scheme, T, soar, .... +;;; This way, all these specific packages share a common base functionality, +;;; and a common set of bindings, which makes them easier to use (and +;;; saves code, implementation time, etc., etc.). + +;;; Several packages are already defined using comint mode: +;;; - cmushell.el defines a shell-in-a-buffer mode. +;;; - cmulisp.el defines a simple lisp-in-a-buffer mode. +;;; Cmushell and cmulisp mode are similar to, and intended to replace, +;;; their counterparts in the standard gnu emacs release (in shell.el). +;;; These replacements are more featureful, robust, and uniform than the +;;; released versions. The key bindings in lisp mode are also more compatible +;;; with the bindings of Hemlock and Zwei (the Lisp Machine emacs). +;;; +;;; - The file cmuscheme.el defines a scheme-in-a-buffer mode. +;;; - The file tea.el tunes scheme and inferior-scheme modes for T. +;;; - The file soar.el tunes lisp and inferior-lisp modes for Soar. +;;; - cmutex.el defines tex and latex modes that invoke tex, latex, bibtex, +;;; previewers, and printers from within emacs. +;;; - background.el allows csh-like job control inside emacs. +;;; It is pretty easy to make new derived modes for other processes. + +;;; For documentation on the functionality provided by comint mode, and +;;; the hooks available for customising it, see the comments below. +;;; For further information on the standard derived modes (shell, +;;; inferior-lisp, inferior-scheme, ...), see the relevant source files. + +;;; For hints on converting existing process modes (e.g., tex-mode, +;;; background, dbx, gdb, kermit, prolog, telnet) to use comint-mode +;;; instead of shell-mode, see the notes at the end of this file. + +(provide 'comint) +(defconst comint-version "2.03") + + +;;; Brief Command Documentation: +;;;============================================================================ +;;; Comint Mode Commands: (common to all derived modes, like cmushell & cmulisp +;;; mode) +;;; +;;; m-p comint-previous-input Cycle backwards in input history +;;; m-n comint-next-input Cycle forwards +;;; m-s comint-previous-similar-input Previous similar input +;;; c-m-r comint-previous-input-matching Search backwards in input history +;;; return comint-send-input +;;; c-a comint-bol Beginning of line; skip prompt. +;;; c-d comint-delchar-or-maybe-eof Delete char unless at end of buff. +;;; c-c c-u comint-kill-input ^u +;;; c-c c-w backward-kill-word ^w +;;; c-c c-c comint-interrupt-subjob ^c +;;; c-c c-z comint-stop-subjob ^z +;;; c-c c-\ comint-quit-subjob ^\ +;;; c-c c-o comint-kill-output Delete last batch of process output +;;; c-c c-r comint-show-output Show last batch of process output +;;; +;;; Not bound by default in comint-mode +;;; send-invisible Read a line w/o echo, and send to proc +;;; (These are bound in shell-mode) +;;; comint-dynamic-complete Complete filename at point. +;;; comint-dynamic-list-completions List completions in help buffer. +;;; comint-replace-by-expanded-filename Expand and complete filename at point; +;;; replace with expanded/completed name. +;;; comint-kill-subjob No mercy. +;;; comint-continue-subjob Send CONT signal to buffer's process +;;; group. Useful if you accidentally +;;; suspend your process (with C-c C-z). +;;; +;;; These used to be bound for RMS -- I prefer the input history stuff, +;;; but you might like 'em. +;;; m-P comint-msearch-input Search backwards for prompt +;;; m-N comint-psearch-input Search forwards for prompt +;;; C-cR comint-msearch-input-matching Search backwards for prompt & string + +;;; comint-mode-hook is the comint mode hook. Basically for your keybindings. +;;; comint-load-hook is run after loading in this package. + + +;;; Buffer Local Variables: +;;;============================================================================ +;;; Comint mode buffer local variables: +;;; comint-prompt-regexp - string comint-bol uses to match prompt. +;;; comint-last-input-end - marker For comint-kill-output command +;;; input-ring-size - integer For the input history +;;; input-ring - ring mechanism +;;; input-ring-index - marker ... +;;; comint-last-input-match - string ... +;;; comint-get-old-input - function Hooks for specific +;;; comint-input-sentinel - function process-in-a-buffer +;;; comint-input-filter - function modes. +;;; comint-input-send - function +;;; comint-eol-on-send - boolean + +(defvar comint-prompt-regexp "^" + "Regexp to recognise prompts in the inferior process. +Defaults to \"^\", the null string at BOL. + +Good choices: + Canonical Lisp: \"^[^> ]*>+:? *\" (Lucid, franz, kcl, T, cscheme, oaklisp) + Lucid Common Lisp: \"^\\(>\\|\\(->\\)+\\) *\" + franz: \"^\\(->\\|<[0-9]*>:\\) *\" + kcl: \"^>+ *\" + shell: \"^[^#$%>]*[#$%>] *\" + T: \"^>+ *\" + +This is a good thing to set in mode hooks.") + +(defvar input-ring-size 30 + "Size of input history ring.") + +;;; Here are the per-interpreter hooks. +(defvar comint-get-old-input (function comint-get-old-input-default) + "Function that submits old text in comint mode. +This function is called when return is typed while the point is in old text. +It returns the text to be submitted as process input. The default is +comint-get-old-input-default, which grabs the current line, and strips off +leading text matching comint-prompt-regexp") + +(defvar comint-input-sentinel (function ignore) + "Called on each input submitted to comint mode process by comint-send-input. +Thus it can, for instance, track cd/pushd/popd commands issued to the csh.") + +(defvar comint-input-filter + (function (lambda (str) (not (string-match "\\`\\s *\\'" str)))) + "Predicate for filtering additions to input history. +Only inputs answering true to this function are saved on the input +history list. Default is to save anything that isn't all whitespace") + +(defvar comint-input-sender (function comint-simple-send) + "Function to actually send to PROCESS the STRING submitted by user. +Usually this is just 'comint-simple-send, but if your mode needs to +massage the input string, this is your hook. This is called from +the user command comint-send-input. comint-simple-send just sends +the string plus a newline.") + +(defvar comint-eol-on-send 'T + "If non-nil, then jump to the end of the line before sending input to process. +See COMINT-SEND-INPUT") + +(defvar comint-mode-hook '() + "Called upon entry into comint-mode +This is run before the process is cranked up.") + +(defvar comint-exec-hook '() + "Called each time a process is exec'd by comint-exec. +This is called after the process is cranked up. It is useful for things that +must be done each time a process is executed in a comint-mode buffer (e.g., +(process-kill-without-query)). In contrast, the comint-mode-hook is only +executed once when the buffer is created.") + +(defvar comint-mode-map nil) + +(defun comint-mode () + "Major mode for interacting with an inferior interpreter. +Interpreter name is same as buffer name, sans the asterisks. +Return at end of buffer sends line as input. +Return not at end copies rest of line to end and sends it. +Setting mode variable comint-eol-on-send means jump to the end of the line +before submitting new input. + +This mode is typically customised to create inferior-lisp-mode, +shell-mode, etc.. This can be done by setting the hooks +comint-input-sentinel, comint-input-filter, comint-input-sender and +comint-get-old-input to appropriate functions, and the variable +comint-prompt-regexp to the appropriate regular expression. + +An input history is maintained of size input-ring-size, and +can be accessed with the commands comint-next-input [\\[comint-next-input]] and +comint-previous-input [\\[comint-previous-input]]. Commands not keybound by +default are send-invisible, comint-dynamic-complete, and +comint-list-dynamic-completions. + +If you accidentally suspend your process, use \\[comint-continue-subjob] +to continue it. + +\\{comint-mode-map} + +Entry to this mode runs the hooks on comint-mode-hook" + (interactive) + (let ((old-ring (and (assq 'input-ring (buffer-local-variables)) + (boundp 'input-ring) + input-ring)) + (old-ptyp comint-ptyp)) ; preserve across local var kill. gross. +; (kill-all-local-variables) ; Removed 1/15/90 Olin + (setq major-mode 'comint-mode) + (setq mode-name "Comint") + (setq mode-line-process '(": %s")) + (use-local-map comint-mode-map) + (make-local-variable 'comint-last-input-end) + (setq comint-last-input-end (make-marker)) + (make-local-variable 'comint-last-input-match) + (setq comint-last-input-match "") + (make-local-variable 'comint-prompt-regexp) ; Don't set; default + (make-local-variable 'input-ring-size) ; ...to global val. + (make-local-variable 'input-ring) + (make-local-variable 'input-ring-index) + (setq input-ring-index 0) + (make-local-variable 'comint-get-old-input) + (make-local-variable 'comint-input-sentinel) + (make-local-variable 'comint-input-filter) + (make-local-variable 'comint-input-sender) + (make-local-variable 'comint-eol-on-send) + (make-local-variable 'comint-ptyp) + (setq comint-ptyp old-ptyp) + (make-local-variable 'comint-exec-hook) + (run-hooks 'comint-mode-hook) + ;Do this after the hook so the user can mung INPUT-RING-SIZE w/his hook. + ;The test is so we don't lose history if we run comint-mode twice in + ;a buffer. + (setq input-ring (if (ring-p old-ring) old-ring + (make-ring input-ring-size))))) + +;;; The old-ptyp stuff above is because we have to preserve the value of +;;; comint-ptyp across calls to comint-mode, in spite of the +;;; kill-all-local-variables that it does. Blech. Hopefully, this will all +;;; go away when a later release fixes the signalling bug. +;;; (Later: I removed the kill-all-local-variables, but have left this +;;; other code in place just in case I reverse myself.) + +(if comint-mode-map + nil + (setq comint-mode-map (make-sparse-keymap)) + (define-key comint-mode-map "\ep" 'comint-previous-input) + (define-key comint-mode-map "\en" 'comint-next-input) + (define-key comint-mode-map "\es" 'comint-previous-similar-input) + (define-key comint-mode-map "\C-m" 'comint-send-input) + (define-key comint-mode-map "\C-d" 'comint-delchar-or-maybe-eof) + (define-key comint-mode-map "\C-a" 'comint-bol) + (define-key comint-mode-map "\C-c\C-u" 'comint-kill-input) + (define-key comint-mode-map "\C-c\C-w" 'backward-kill-word) + (define-key comint-mode-map "\C-c\C-c" 'comint-interrupt-subjob) + (define-key comint-mode-map "\C-c\C-z" 'comint-stop-subjob) + (define-key comint-mode-map "\C-c\C-\\" 'comint-quit-subjob) + (define-key comint-mode-map "\C-c\C-o" 'comint-kill-output) + (define-key comint-mode-map "\C-\M-r" 'comint-previous-input-matching) + (define-key comint-mode-map "\C-c\C-r" 'comint-show-output) + ;;; prompt-search commands commented out 3/90 -Olin +; (define-key comint-mode-map "\eP" 'comint-msearch-input) +; (define-key comint-mode-map "\eN" 'comint-psearch-input) +; (define-key comint-mode-map "\C-cR" 'comint-msearch-input-matching) + ) + + +;;; This function is used to make a full copy of the comint mode map, +;;; so that client modes won't interfere with each other. This function +;;; isn't necessary in emacs 18.5x, but we keep it around for 18.4x versions. +(defun full-copy-sparse-keymap (km) + "Recursively copy the sparse keymap KM" + (cond ((consp km) + (cons (full-copy-sparse-keymap (car km)) + (full-copy-sparse-keymap (cdr km)))) + (t km))) + +(defun comint-check-proc (buffer) + "True if there is a process associated w/buffer BUFFER, and +it is alive (status RUN or STOP). BUFFER can be either a buffer or the +name of one" + (let ((proc (get-buffer-process buffer))) + (and proc (memq (process-status proc) '(run stop))))) + +;;; Note that this guy, unlike shell.el's make-shell, barfs if you pass it () +;;; for the second argument (program). +(defun make-comint (name program &optional startfile &rest switches) + (let ((buffer (get-buffer-create (concat "*" name "*")))) + ;; If no process, or nuked process, crank up a new one and put buffer in + ;; comint mode. Otherwise, leave buffer and existing process alone. + (cond ((not (comint-check-proc buffer)) + (save-excursion + (set-buffer buffer) + (comint-mode)) ; Install local vars, mode, keymap, ... + (comint-exec buffer name program startfile switches))) + buffer)) + +(defvar comint-ptyp t + "True if communications via pty; false if by pipe. Buffer local. +This is to work around a bug in emacs process signalling.") + +(defun comint-exec (buffer name command startfile switches) + "Fires up a process in buffer for comint modes. +Blasts any old process running in the buffer. Doesn't set the buffer mode. +You can use this to cheaply run a series of processes in the same comint +buffer. The hook comint-exec-hook is run after each exec." + (save-excursion + (set-buffer buffer) + (let ((proc (get-buffer-process buffer))) ; Blast any old process. + (if proc (delete-process proc))) + ;; Crank up a new process + (let ((proc (comint-exec-1 name buffer command switches))) + (make-local-variable 'comint-ptyp) + (setq comint-ptyp process-connection-type) ; T if pty, NIL if pipe. + ;; Jump to the end, and set the process mark. + (goto-char (point-max)) + (set-marker (process-mark proc) (point)) + ;; Feed it the startfile. + (cond (startfile + ;;This is guaranteed to wait long enough + ;;but has bad results if the comint does not prompt at all + ;; (while (= size (buffer-size)) + ;; (sleep-for 1)) + ;;I hope 1 second is enough! + (sleep-for 1) + (goto-char (point-max)) + (insert-file-contents startfile) + (setq startfile (buffer-substring (point) (point-max))) + (delete-region (point) (point-max)) + (comint-send-string proc startfile))) + (run-hooks 'comint-exec-hook) + buffer))) + +;;; This auxiliary function cranks up the process for comint-exec in +;;; the appropriate environment. It is twice as long as it should be +;;; because emacs has two distinct mechanisms for manipulating the +;;; process environment, selected at compile time with the +;;; MAINTAIN-ENVIRONMENT #define. In one case, process-environment +;;; is bound; in the other it isn't. + +(defun comint-exec-1 (name buffer command switches) + (if (boundp 'process-environment) ; Not a completely reliable test. + (let ((process-environment + (comint-update-env process-environment + (list (format "TERMCAP=emacs:co#%d:tc=unknown" + (screen-width)) + "TERM=emacs" + "EMACS=t")))) + (apply 'start-process name buffer command switches)) + + (let ((tcapv (getenv "TERMCAP")) + (termv (getenv "TERM")) + (emv (getenv "EMACS"))) + (unwind-protect + (progn (setenv "TERMCAP" (format "emacs:co#%d:tc=unknown" + (screen-width))) + (setenv "TERM" "emacs") + (setenv "EMACS" "t") + (apply 'start-process name buffer command switches)) + (setenv "TERMCAP" tcapv) + (setenv "TERM" termv) + (setenv "EMACS" emv))))) + + + +;; This is just (append new old-env) that compresses out shadowed entries. +;; It's also pretty ugly, mostly due to elisp's horrible iteration structures. +(defun comint-update-env (old-env new) + (let ((ans (reverse new)) + (vars (mapcar (function (lambda (vv) + (and (string-match "^[^=]*=" vv) + (substring vv 0 (match-end 0))))) + new))) + (while old-env + (let* ((vv (car old-env)) ; vv is var=value + (var (and (string-match "^[^=]*=" vv) + (substring vv 0 (match-end 0))))) + (setq old-env (cdr old-env)) + (cond ((not (and var (comint-mem var vars))) + (if var (setq var (cons var vars))) + (setq ans (cons vv ans)))))) + (nreverse ans))) + +;;; This should be in emacs, but it isn't. +(defun comint-mem (item list &optional elt=) + "Test to see if ITEM is equal to an item in LIST. +Option comparison function ELT= defaults to equal." + (let ((elt= (or elt= (function equal))) + (done nil)) + (while (and list (not done)) + (if (funcall elt= item (car list)) + (setq done list) + (setq list (cdr list)))) + done)) + + +;;; Ring Code +;;;============================================================================ +;;; This code defines a ring data structure. A ring is a +;;; (hd-index tl-index . vector) +;;; list. You can insert to, remove from, and rotate a ring. When the ring +;;; fills up, insertions cause the oldest elts to be quietly dropped. +;;; +;;; HEAD = index of the newest item on the ring. +;;; TAIL = index of the oldest item on the ring. +;;; +;;; These functions are used by the input history mechanism, but they can +;;; be used for other purposes as well. + +(defun ring-p (x) + "T if X is a ring; NIL otherwise." + (and (consp x) (integerp (car x)) + (consp (cdr x)) (integerp (car (cdr x))) + (vectorp (cdr (cdr x))))) + +(defun make-ring (size) + "Make a ring that can contain SIZE elts" + (cons 1 (cons 0 (make-vector (+ size 1) nil)))) + +(defun ring-plus1 (index veclen) + "INDEX+1, with wraparound" + (let ((new-index (+ index 1))) + (if (= new-index veclen) 0 new-index))) + +(defun ring-minus1 (index veclen) + "INDEX-1, with wraparound" + (- (if (= 0 index) veclen index) 1)) + +(defun ring-length (ring) + "Number of elts in the ring." + (let ((hd (car ring)) (tl (car (cdr ring))) (siz (length (cdr (cdr ring))))) + (let ((len (if (<= hd tl) (+ 1 (- tl hd)) (+ 1 tl (- siz hd))))) + (if (= len siz) 0 len)))) + +(defun ring-empty-p (ring) + (= 0 (ring-length ring))) + +(defun ring-insert (ring item) + "Insert a new item onto the ring. If the ring is full, dump the oldest +item to make room." + (let* ((vec (cdr (cdr ring))) (len (length vec)) + (new-hd (ring-minus1 (car ring) len))) + (setcar ring new-hd) + (aset vec new-hd item) + (if (ring-empty-p ring) ;overflow -- dump one off the tail. + (setcar (cdr ring) (ring-minus1 (car (cdr ring)) len))))) + +(defun ring-remove (ring) + "Remove the oldest item retained on the ring." + (if (ring-empty-p ring) (error "Ring empty") + (let ((tl (car (cdr ring))) (vec (cdr (cdr ring)))) + (set-car (cdr ring) (ring-minus1 tl (length vec))) + (aref vec tl)))) + +;;; This isn't actually used in this package. I just threw it in in case +;;; someone else wanted it. If you want rotating-ring behavior on your history +;;; retrieval (analagous to kill ring behavior), this function is what you +;;; need. I should write the yank-input and yank-pop-input-or-kill to go with +;;; this, and not bind it to a key by default, so it would be available to +;;; people who want to bind it to a key. But who would want it? Blech. +(defun ring-rotate (ring n) + (if (not (= n 0)) + (if (ring-empty-p ring) ;Is this the right error check? + (error "ring empty") + (let ((hd (car ring)) (tl (car (cdr ring))) (vec (cdr (cdr ring)))) + (let ((len (length vec))) + (while (> n 0) + (setq tl (ring-plus1 tl len)) + (aset ring tl (aref ring hd)) + (setq hd (ring-plus1 hd len)) + (setq n (- n 1))) + (while (< n 0) + (setq hd (ring-minus1 hd len)) + (aset vec hd (aref vec tl)) + (setq tl (ring-minus1 tl len)) + (setq n (- n 1)))) + (set-car ring hd) + (set-car (cdr ring) tl))))) + +(defun comint-mod (n m) + "Returns N mod M. M is positive. Answer is guaranteed to be non-negative, +and less than m." + (let ((n (% n m))) + (if (>= n 0) n + (+ n + (if (>= m 0) m (- m)))))) ; (abs m) + +(defun ring-ref (ring index) + (let ((numelts (ring-length ring))) + (if (= numelts 0) (error "indexed empty ring") + (let* ((hd (car ring)) (tl (car (cdr ring))) (vec (cdr (cdr ring))) + (index (comint-mod index numelts)) + (vec-index (comint-mod (+ index hd) + (length vec)))) + (aref vec vec-index))))) + + +;;; Input history retrieval commands +;;; M-p -- previous input M-n -- next input +;;; M-C-r -- previous input matching +;;; =========================================================================== + +(defun comint-previous-input (arg) + "Cycle backwards through input history." + (interactive "*p") + (let ((len (ring-length input-ring))) + (cond ((<= len 0) + (message "Empty input ring") + (ding)) + ((not (comint-after-pmark-p)) + (message "Not after process mark") + (ding)) + (t + (cond ((eq last-command 'comint-previous-input) + (delete-region (mark) (point))) + ((eq last-command 'comint-previous-similar-input) + (delete-region + (process-mark (get-buffer-process (current-buffer))) + (point))) + (t + (setq input-ring-index + (if (> arg 0) -1 + (if (< arg 0) 1 0))) + (push-mark (point)))) + (setq input-ring-index (comint-mod (+ input-ring-index arg) len)) + (message "%d" (1+ input-ring-index)) + (insert (ring-ref input-ring input-ring-index)) + (setq this-command 'comint-previous-input))))) + +(defun comint-next-input (arg) + "Cycle forwards through input history." + (interactive "*p") + (comint-previous-input (- arg))) + +(defvar comint-last-input-match "" + "Last string searched for by comint input history search, for defaulting. +Buffer local variable.") + +(defun comint-previous-input-matching (str) + "Searches backwards through input history for substring match." + (interactive (let* ((last-command last-command) ; preserve around r-f-m + (s (read-from-minibuffer + (format "Command substring (default %s): " + comint-last-input-match)))) + (list (if (string= s "") comint-last-input-match s)))) +; (interactive "sCommand substring: ") + (setq comint-last-input-match str) ; update default + (if (not (eq last-command 'comint-previous-input)) + (setq input-ring-index -1)) + (let ((str (regexp-quote str)) + (len (ring-length input-ring)) + (n (+ input-ring-index 1))) + (while (and (< n len) (not (string-match str (ring-ref input-ring n)))) + (setq n (+ n 1))) + (cond ((< n len) + (comint-previous-input (- n input-ring-index))) + (t (if (eq last-command 'comint-previous-input) + (setq this-command 'comint-previous-input)) + (message "Not found.") + (ding))))) + + +;;; These next three commands are alternatives to the input history commands +;;; -- comint-next-input, comint-previous-input and +;;; comint-previous-input-matching. They search through the process buffer +;;; text looking for occurrences of the prompt. Bound to M-P, M-N, and C-c R +;;; (uppercase P, N, and R) for now. Try'em out. Go with what you like... + +;;; comint-msearch-input-matching prompts for a string, not a regexp. +;;; This could be considered to be the wrong thing. I decided to keep it +;;; simple, and not make the user worry about regexps. This, of course, +;;; limits functionality. + +;;; These commands were deemed non-winning and have been commented out. +;;; Feel free to re-enable them if you like. -Olin 3/91 + +;(defun comint-psearch-input () +; "Search forwards for next occurrence of prompt and skip to end of line. +;\(prompt is anything matching regexp comint-prompt-regexp)" +; (interactive) +; (if (re-search-forward comint-prompt-regexp (point-max) t) +; (end-of-line) +; (error "No occurrence of prompt found"))) +; +;(defun comint-msearch-input () +; "Search backwards for previous occurrence of prompt and skip to end of line. +;Search starts from beginning of current line." +; (interactive) +; (let ((p (save-excursion +; (beginning-of-line) +; (cond ((re-search-backward comint-prompt-regexp (point-min) t) +; (end-of-line) +; (point)) +; (t nil))))) +; (if p (goto-char p) +; (error "No occurrence of prompt found")))) +; +;(defun comint-msearch-input-matching (str) +; "Search backwards for occurrence of prompt followed by STRING. +;STRING is prompted for, and is NOT a regular expression." +; (interactive (let ((s (read-from-minibuffer +; (format "Command (default %s): " +; comint-last-input-match)))) +; (list (if (string= s "") comint-last-input-match s)))) +;; (interactive "sCommand: ") +; (setq comint-last-input-match str) ; update default +; (let* ((r (concat comint-prompt-regexp (regexp-quote str))) +; (p (save-excursion +; (beginning-of-line) +; (cond ((re-search-backward r (point-min) t) +; (end-of-line) +; (point)) +; (t nil))))) +; (if p (goto-char p) +; (error "No match")))) + +;;; +;;; Similar input -- contributed by ccm and highly winning. +;;; +;;; Reenter input, removing back to the last insert point if it exists. +;;; +(defvar comint-last-similar-string "" + "The string last used in a similar string search.") +(defun comint-previous-similar-input (arg) + "Reenters the last input that matches the string typed so far. If repeated +successively older inputs are reentered. If arg is 1, it will go back +in the history, if -1 it will go forward." + (interactive "p") + (if (not (comint-after-pmark-p)) + (error "Not after process mark")) + (if (not (eq last-command 'comint-previous-similar-input)) + (setq input-ring-index -1 + comint-last-similar-string + (buffer-substring + (process-mark (get-buffer-process (current-buffer))) + (point)))) + (let* ((size (length comint-last-similar-string)) + (len (ring-length input-ring)) + (n (+ input-ring-index arg)) + entry) + (while (and (< n len) + (or (< (length (setq entry (ring-ref input-ring n))) size) + (not (equal comint-last-similar-string + (substring entry 0 size))))) + (setq n (+ n arg))) + (cond ((< n len) + (setq input-ring-index n) + (if (eq last-command 'comint-previous-similar-input) + (delete-region (mark) (point)) ; repeat + (push-mark (point))) ; 1st time + (insert (substring entry size))) + (t (message "Not found.") (ding) (sit-for 1))) + (message "%d" (1+ input-ring-index)))) + + +(defun comint-send-input () + "Send input to process. After the process output mark, sends all text +>from the process mark to point as input to the process. Before the process +output mark, calls value of variable comint-get-old-input to retrieve old +input, copies it to the process mark, and sends it. A terminal newline is +also inserted into the buffer and sent to the process. In either case, value +of variable comint-input-sentinel is called on the input before sending it. +The input is entered into the input history ring, if the value of variable +comint-input-filter returns non-nil when called on the input. + +If variable comint-eol-on-send is non-nil, then point is moved to the end of +line before sending the input. + +comint-get-old-input, comint-input-sentinel, and comint-input-filter are chosen +according to the command interpreter running in the buffer. E.g., +If the interpreter is the csh, + comint-get-old-input is the default: take the current line, discard any + initial string matching regexp comint-prompt-regexp. + comint-input-sentinel monitors input for \"cd\", \"pushd\", and \"popd\" + commands. When it sees one, it cd's the buffer. + comint-input-filter is the default: returns T if the input isn't all white + space. + +If the comint is Lucid Common Lisp, + comint-get-old-input snarfs the sexp ending at point. + comint-input-sentinel does nothing. + comint-input-filter returns NIL if the input matches input-filter-regexp, + which matches (1) all whitespace (2) :a, :c, etc. + +Similarly for Soar, Scheme, etc.." + (interactive) + ;; Note that the input string does not include its terminal newline. + (let ((proc (get-buffer-process (current-buffer)))) + (if (not proc) (error "Current buffer has no process") + (let* ((pmark (process-mark proc)) + (pmark-val (marker-position pmark)) + (input (if (>= (point) pmark-val) + (progn (if comint-eol-on-send (end-of-line)) + (buffer-substring pmark (point))) + (let ((copy (funcall comint-get-old-input))) + (goto-char pmark) + (insert copy) + copy)))) + (insert ?\n) + (if (funcall comint-input-filter input) (ring-insert input-ring input)) + (funcall comint-input-sentinel input) + (funcall comint-input-sender proc input) + (set-marker (process-mark proc) (point)) + (set-marker comint-last-input-end (point)))))) + +(defun comint-get-old-input-default () + "Default for comint-get-old-input: take the current line, and discard +any initial text matching comint-prompt-regexp." + (save-excursion + (beginning-of-line) + (comint-skip-prompt) + (let ((beg (point))) + (end-of-line) + (buffer-substring beg (point))))) + +(defun comint-skip-prompt () + "Skip past the text matching regexp comint-prompt-regexp. +If this takes us past the end of the current line, don't skip at all." + (let ((eol (save-excursion (end-of-line) (point)))) + (if (and (looking-at comint-prompt-regexp) + (<= (match-end 0) eol)) + (goto-char (match-end 0))))) + + +(defun comint-after-pmark-p () + "Is point after the process output marker?" + ;; Since output could come into the buffer after we looked at the point + ;; but before we looked at the process marker's value, we explicitly + ;; serialise. This is just because I don't know whether or not emacs + ;; services input during execution of lisp commands. + (let ((proc-pos (marker-position + (process-mark (get-buffer-process (current-buffer)))))) + (<= proc-pos (point)))) + +(defun comint-simple-send (proc string) + "Default function for sending to PROC input STRING. +This just sends STRING plus a newline. To override this, +set the hook COMINT-INPUT-SENDER." + (comint-send-string proc string) + (comint-send-string proc "\n")) + +(defun comint-bol (arg) + "Goes to the beginning of line, then skips past the prompt, if any. +If a prefix argument is given (\\[universal-argument]), then no prompt skip +-- go straight to column 0. + +The prompt skip is done by skipping text matching the regular expression +comint-prompt-regexp, a buffer local variable. + +If you don't like this command, reset c-a to beginning-of-line +in your hook, comint-mode-hook." + (interactive "P") + (beginning-of-line) + (if (null arg) (comint-skip-prompt))) + +;;; These two functions are for entering text you don't want echoed or +;;; saved -- typically passwords to ftp, telnet, or somesuch. +;;; Just enter m-x send-invisible and type in your line. + +(defun comint-read-noecho (prompt &optional stars) + "Prompt the user with argument PROMPT. Read a single line of text +without echoing, and return it. Note that the keystrokes comprising +the text can still be recovered (temporarily) with \\[view-lossage]. This +may be a security bug for some applications. Optional argument STARS +causes input to be echoed with '*' characters on the prompt line." + (let ((echo-keystrokes 0) + (cursor-in-echo-area t) + (answ "") + tem) + (if (not (stringp prompt)) (setq prompt "")) + (message prompt) + (while (not(or (= (setq tem (read-char)) ?\^m) + (= tem ?\n))) + (setq answ (concat answ (char-to-string tem))) + (if stars (setq prompt (concat prompt "*"))) + (message prompt)) + (message "") + answ)) + + +(defun send-invisible (str) + "Read a string without echoing, and send it to the process running +in the current buffer. A new-line is additionally sent. String is not +saved on comint input history list. +Security bug: your string can still be temporarily recovered with +\\[view-lossage]." +; (interactive (list (comint-read-noecho "Enter non-echoed text"))) + (interactive "P") ; Defeat snooping via C-x esc + (let ((proc (get-buffer-process (current-buffer)))) + (if (not proc) (error "Current buffer has no process") + (comint-send-string proc + (if (stringp str) str + (comint-read-noecho "Non-echoed text: " t))) + (comint-send-string proc "\n")))) + + +;;; Low-level process communication + +(defvar comint-input-chunk-size 512 + "*Long inputs send to comint processes are broken up into chunks of this size. +If your process is choking on big inputs, try lowering the value.") + +(defun comint-send-string (proc str) + "Send PROCESS the contents of STRING as input. +This is equivalent to process-send-string, except that long input strings +are broken up into chunks of size comint-input-chunk-size. Processes +are given a chance to output between chunks. This can help prevent processes +>from hanging when you send them long inputs on some OS's." + (let* ((len (length str)) + (i (min len comint-input-chunk-size))) + (process-send-string proc (substring str 0 i)) + (while (< i len) + (let ((next-i (+ i comint-input-chunk-size))) + (accept-process-output) + (process-send-string proc (substring str i (min len next-i))) + (setq i next-i))))) + +(defun comint-send-region (proc start end) + "Sends to PROC the region delimited by START and END. +This is a replacement for process-send-region that tries to keep +your process from hanging on long inputs. See comint-send-string." + (comint-send-string proc (buffer-substring start end))) + + +;;; Random input hackage + +(defun comint-kill-output () + "Kill all output from interpreter since last input." + (interactive) + (let ((pmark (process-mark (get-buffer-process (current-buffer))))) + (kill-region comint-last-input-end pmark) + (goto-char pmark) + (insert "*** output flushed ***\n") + (set-marker pmark (point)))) + +(defun comint-show-output () + "Display start of this batch of interpreter output at top of window. +Also put cursor there." + (interactive) + (goto-char comint-last-input-end) + (backward-char) + (beginning-of-line) + (set-window-start (selected-window) (point)) + (end-of-line)) + +(defun comint-interrupt-subjob () + "Interrupt the current subjob." + (interactive) + (interrupt-process nil comint-ptyp)) + +(defun comint-kill-subjob () + "Send kill signal to the current subjob." + (interactive) + (kill-process nil comint-ptyp)) + +(defun comint-quit-subjob () + "Send quit signal to the current subjob." + (interactive) + (quit-process nil comint-ptyp)) + +(defun comint-stop-subjob () + "Stop the current subjob. +WARNING: if there is no current subjob, you can end up suspending +the top-level process running in the buffer. If you accidentally do +this, use \\[comint-continue-subjob] to resume the process. (This +is not a problem with most shells, since they ignore this signal.)" + (interactive) + (stop-process nil comint-ptyp)) + +(defun comint-continue-subjob () + "Send CONT signal to process buffer's process group. +Useful if you accidentally suspend the top-level process." + (interactive) + (continue-process nil comint-ptyp)) + +(defun comint-kill-input () + "Kill all text from last stuff output by interpreter to point." + (interactive) + (let* ((pmark (process-mark (get-buffer-process (current-buffer)))) + (p-pos (marker-position pmark))) + (if (> (point) p-pos) + (kill-region pmark (point))))) + +(defun comint-delchar-or-maybe-eof (arg) + "Delete ARG characters forward, or send an EOF to process if at end of buffer." + (interactive "p") + (if (eobp) + (process-send-eof) + (delete-char arg))) + + + + +;;; Support for source-file processing commands. +;;;============================================================================ +;;; Many command-interpreters (e.g., Lisp, Scheme, Soar) have +;;; commands that process files of source text (e.g. loading or compiling +;;; files). So the corresponding process-in-a-buffer modes have commands +;;; for doing this (e.g., lisp-load-file). The functions below are useful +;;; for defining these commands. +;;; +;;; Alas, these guys don't do exactly the right thing for Lisp, Scheme +;;; and Soar, in that they don't know anything about file extensions. +;;; So the compile/load interface gets the wrong default occasionally. +;;; The load-file/compile-file default mechanism could be smarter -- it +;;; doesn't know about the relationship between filename extensions and +;;; whether the file is source or executable. If you compile foo.lisp +;;; with compile-file, then the next load-file should use foo.bin for +;;; the default, not foo.lisp. This is tricky to do right, particularly +;;; because the extension for executable files varies so much (.o, .bin, +;;; .lbin, .mo, .vo, .ao, ...). + + +;;; COMINT-SOURCE-DEFAULT -- determines defaults for source-file processing +;;; commands. +;;; +;;; COMINT-CHECK-SOURCE -- if FNAME is in a modified buffer, asks you if you +;;; want to save the buffer before issuing any process requests to the command +;;; interpreter. +;;; +;;; COMINT-GET-SOURCE -- used by the source-file processing commands to prompt +;;; for the file to process. + +;;; (COMINT-SOURCE-DEFAULT previous-dir/file source-modes) +;;;============================================================================ +;;; This function computes the defaults for the load-file and compile-file +;;; commands for tea, soar, cmulisp, and cmuscheme modes. +;;; +;;; - PREVIOUS-DIR/FILE is a pair (directory . filename) from the last +;;; source-file processing command. NIL if there hasn't been one yet. +;;; - SOURCE-MODES is a list used to determine what buffers contain source +;;; files: if the major mode of the buffer is in SOURCE-MODES, it's source. +;;; Typically, (lisp-mode) or (scheme-mode). +;;; +;;; If the command is given while the cursor is inside a string, *and* +;;; the string is an existing filename, *and* the filename is not a directory, +;;; then the string is taken as default. This allows you to just position +;;; your cursor over a string that's a filename and have it taken as default. +;;; +;;; If the command is given in a file buffer whose major mode is in +;;; SOURCE-MODES, then the the filename is the default file, and the +;;; file's directory is the default directory. +;;; +;;; If the buffer isn't a source file buffer (e.g., it's the process buffer), +;;; then the default directory & file are what was used in the last source-file +;;; processing command (i.e., PREVIOUS-DIR/FILE). If this is the first time +;;; the command has been run (PREVIOUS-DIR/FILE is nil), the default directory +;;; is the cwd, with no default file. (\"no default file\" = nil) +;;; +;;; SOURCE-REGEXP is typically going to be something like (tea-mode) +;;; for T programs, (lisp-mode) for Lisp programs, (soar-mode lisp-mode) +;;; for Soar programs, etc. +;;; +;;; The function returns a pair: (default-directory . default-file). + +(defun comint-source-default (previous-dir/file source-modes) + (cond ((and buffer-file-name (memq major-mode source-modes)) + (cons (file-name-directory buffer-file-name) + (file-name-nondirectory buffer-file-name))) + (previous-dir/file) + (t + (cons default-directory nil)))) + + +;;; (COMINT-CHECK-SOURCE fname) +;;;============================================================================ +;;; Prior to loading or compiling (or otherwise processing) a file (in the CMU +;;; process-in-a-buffer modes), this function can be called on the filename. +;;; If the file is loaded into a buffer, and the buffer is modified, the user +;;; is queried to see if he wants to save the buffer before proceeding with +;;; the load or compile. + +(defun comint-check-source (fname) + (let ((buff (get-file-buffer fname))) + (if (and buff + (buffer-modified-p buff) + (y-or-n-p (format "Save buffer %s first? " + (buffer-name buff)))) + ;; save BUFF. + (let ((old-buffer (current-buffer))) + (set-buffer buff) + (save-buffer) + (set-buffer old-buffer))))) + + +;;; (COMINT-GET-SOURCE prompt prev-dir/file source-modes mustmatch-p) +;;;============================================================================ +;;; COMINT-GET-SOURCE is used to prompt for filenames in command-interpreter +;;; commands that process source files (like loading or compiling a file). +;;; It prompts for the filename, provides a default, if there is one, +;;; and returns the result filename. +;;; +;;; See COMINT-SOURCE-DEFAULT for more on determining defaults. +;;; +;;; PROMPT is the prompt string. PREV-DIR/FILE is the (directory . file) pair +;;; from the last source processing command. SOURCE-MODES is a list of major +;;; modes used to determine what file buffers contain source files. (These +;;; two arguments are used for determining defaults). If MUSTMATCH-P is true, +;;; then the filename reader will only accept a file that exists. +;;; +;;; A typical use: +;;; (interactive (comint-get-source "Compile file: " prev-lisp-dir/file +;;; '(lisp-mode) t)) + +;;; This is pretty stupid about strings. It decides we're in a string +;;; if there's a quote on both sides of point on the current line. +(defun comint-extract-string () + "Returns string around point that starts the current line or nil." + (save-excursion + (let* ((point (point)) + (bol (progn (beginning-of-line) (point))) + (eol (progn (end-of-line) (point))) + (start (progn (goto-char point) + (and (search-backward "\"" bol t) + (1+ (point))))) + (end (progn (goto-char point) + (and (search-forward "\"" eol t) + (1- (point)))))) + (and start end + (buffer-substring start end))))) + +(defun comint-get-source (prompt prev-dir/file source-modes mustmatch-p) + (let* ((def (comint-source-default prev-dir/file source-modes)) + (stringfile (comint-extract-string)) + (sfile-p (and stringfile + (condition-case () + (file-exists-p stringfile) + (error nil)) + (not (file-directory-p stringfile)))) + (defdir (if sfile-p (file-name-directory stringfile) + (car def))) + (deffile (if sfile-p (file-name-nondirectory stringfile) + (cdr def))) + (ans (read-file-name (if deffile (format "%s(default %s) " + prompt deffile) + prompt) + defdir + (concat defdir deffile) + mustmatch-p))) + (list (expand-file-name (substitute-in-file-name ans))))) + +;;; I am somewhat divided on this string-default feature. It seems +;;; to violate the principle-of-least-astonishment, in that it makes +;;; the default harder to predict, so you actually have to look and see +;;; what the default really is before choosing it. This can trip you up. +;;; On the other hand, it can be useful, I guess. I would appreciate feedback +;;; on this. +;;; -Olin + + +;;; Simple process query facility. +;;; =========================================================================== +;;; This function is for commands that want to send a query to the process +;;; and show the response to the user. For example, a command to get the +;;; arglist for a Common Lisp function might send a "(arglist 'foo)" query +;;; to an inferior Common Lisp process. +;;; +;;; This simple facility just sends strings to the inferior process and pops +;;; up a window for the process buffer so you can see what the process +;;; responds with. We don't do anything fancy like try to intercept what the +;;; process responds with and put it in a pop-up window or on the message +;;; line. We just display the buffer. Low tech. Simple. Works good. + +;;; Send to the inferior process PROC the string STR. Pop-up but do not select +;;; a window for the inferior process so that its response can be seen. +(defun comint-proc-query (proc str) + (let* ((proc-buf (process-buffer proc)) + (proc-mark (process-mark proc))) + (display-buffer proc-buf) + (set-buffer proc-buf) ; but it's not the selected *window* + (let ((proc-win (get-buffer-window proc-buf)) + (proc-pt (marker-position proc-mark))) + (comint-send-string proc str) ; send the query + (accept-process-output proc) ; wait for some output + ;; Try to position the proc window so you can see the answer. + ;; This is bogus code. If you delete the (sit-for 0), it breaks. + ;; I don't know why. Wizards invited to improve it. + (if (not (pos-visible-in-window-p proc-pt proc-win)) + (let ((opoint (window-point proc-win))) + (set-window-point proc-win proc-mark) (sit-for 0) + (if (not (pos-visible-in-window-p opoint proc-win)) + (push-mark opoint) + (set-window-point proc-win opoint))))))) + + +;;; Filename completion in a buffer +;;; =========================================================================== +;;; Useful completion functions, courtesy of the Ergo group. +;;; M- will complete the filename at the cursor as much as possible +;;; M-? will display a list of completions in the help buffer. + +;;; Three commands: +;;; comint-dynamic-complete Complete filename at point. +;;; comint-dynamic-list-completions List completions in help buffer. +;;; comint-replace-by-expanded-filename Expand and complete filename at point; +;;; replace with expanded/completed name. + +;;; These are not installed in the comint-mode keymap. But they are +;;; available for people who want them. Shell-mode installs them: +;;; (define-key cmushell-mode-map "\M-\t" 'comint-dynamic-complete) +;;; (define-key cmushell-mode-map "\M-?" 'comint-dynamic-list-completions))) +;;; +;;; Commands like this are fine things to put in load hooks if you +;;; want them present in specific modes. Example: +;;; (setq cmushell-load-hook +;;; '((lambda () (define-key lisp-mode-map "\M-\t" +;;; 'comint-replace-by-expanded-filename)))) +;;; + + +(defun comint-match-partial-pathname () + "Returns the filename at point or causes an error." + (save-excursion + (if (re-search-backward "[^~/A-Za-z0-9---_.$#,=]" nil 'move) + (forward-char 1)) + ;; Anchor the search forwards. + (if (not (looking-at "[~/A-Za-z0-9---_.$#,=]")) (error "")) + (re-search-forward "[~/A-Za-z0-9---_.$#,=]+") + (substitute-in-file-name + (buffer-substring (match-beginning 0) (match-end 0))))) + + +(defun comint-replace-by-expanded-filename () +"Replace the filename at point with an expanded, canonicalised, and +completed replacement. +\"Expanded\" means environment variables (e.g., $HOME) and ~'s are +replaced with the corresponding directories. \"Canonicalised\" means .. +and \. are removed, and the filename is made absolute instead of relative. +See functions expand-file-name and substitute-in-file-name. See also +comint-dynamic-complete." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completion (file-name-completion pathnondir + (or pathdir default-directory)))) + (cond ((null completion) + (message "No completions of %s." pathname) + (ding)) + ((eql completion t) + (message "Unique completion.")) + (t ; this means a string was returned. + (delete-region (match-beginning 0) (match-end 0)) + (insert (expand-file-name (concat pathdir completion))))))) + + +(defun comint-dynamic-complete () + "Dynamically complete the filename at point. +This function is similar to comint-replace-by-expanded-filename, except +that it won't change parts of the filename already entered in the buffer; +it just adds completion characters to the end of the filename." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completion (file-name-completion pathnondir + (or pathdir default-directory)))) + (cond ((null completion) + (message "No completions of %s." pathname) + (ding)) + ((eql completion t) + (message "Unique completion.")) + (t ; this means a string was returned. + (goto-char (match-end 0)) + (insert (substring completion (length pathnondir))))))) + +(defun comint-dynamic-list-completions () + "List in help buffer all possible completions of the filename at point." + (interactive) + (let* ((pathname (comint-match-partial-pathname)) + (pathdir (file-name-directory pathname)) + (pathnondir (file-name-nondirectory pathname)) + (completions + (file-name-all-completions pathnondir + (or pathdir default-directory)))) + (cond ((null completions) + (message "No completions of %s." pathname) + (ding)) + (t + (let ((conf (current-window-configuration))) + (with-output-to-temp-buffer "*Help*" + (display-completion-list completions)) + (sit-for 0) + (message "Hit space to flush.") + (let ((ch (read-char))) + (if (= ch ?\ ) + (set-window-configuration conf) + (setq unread-command-char ch)))))))) + +; Ergo bindings +; (global-set-key "\M-\t" 'comint-replace-by-expanded-filename) +; (global-set-key "\M-?" 'comint-dynamic-list-completions) +; (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete) + +;;; Converting process modes to use comint mode +;;; =========================================================================== +;;; Several gnu packages (tex-mode, background, dbx, gdb, kermit, prolog, +;;; telnet are some) use the shell package as clients. Most of them would +;;; be better off using the comint package, but they predate it. +;;; +;;; Altering these packages to use comint mode should greatly +;;; improve their functionality, and is fairly easy. +;;; +;;; Renaming variables +;;; Most of the work is renaming variables and functions. These are the common +;;; ones: +;;; Local variables: +;;; last-input-end comint-last-input-end +;;; last-input-start +;;; shell-prompt-pattern comint-prompt-regexp +;;; shell-set-directory-error-hook +;;; Miscellaneous: +;;; shell-set-directory +;;; shell-mode-map comint-mode-map +;;; Commands: +;;; shell-send-input comint-send-input +;;; shell-send-eof comint-delchar-or-maybe-eof +;;; kill-shell-input comint-kill-input +;;; interrupt-shell-subjob comint-interrupt-subjob +;;; stop-shell-subjob comint-stop-subjob +;;; quit-shell-subjob comint-quit-subjob +;;; kill-shell-subjob comint-kill-subjob +;;; kill-output-from-shell comint-kill-output +;;; show-output-from-shell comint-show-output +;;; copy-last-shell-input Use comint-previous-input/comint-next-input +;;; +;;; LAST-INPUT-START is no longer necessary because inputs are stored on the +;;; input history ring. SHELL-SET-DIRECTORY is gone, its functionality taken +;;; over by SHELL-DIRECTORY-TRACKER, the shell mode's comint-input-sentinel. +;;; Comint mode does not provide functionality equivalent to +;;; shell-set-directory-error-hook; it is gone. +;;; +;;; If you are implementing some process-in-a-buffer mode, called foo-mode, do +;;; *not* create the comint-mode local variables in your foo-mode function. +;;; This is not modular. Instead, call comint-mode, and let *it* create the +;;; necessary comint-specific local variables. Then create the +;;; foo-mode-specific local variables in foo-mode. Set the buffer's keymap to +;;; be foo-mode-map, and its mode to be foo-mode. Set the comint-mode hooks +;;; (comint-prompt-regexp, comint-input-filter, comint-input-sentinel, +;;; comint-get-old-input) that need to be different from the defaults. Call +;;; foo-mode-hook, and you're done. Don't run the comint-mode hook yourself; +;;; comint-mode will take care of it. The following example, from cmushell.el, +;;; is typical: +;;; +;;; (defun shell-mode () +;;; (interactive) +;;; (comint-mode) +;;; (setq comint-prompt-regexp shell-prompt-pattern) +;;; (setq major-mode 'shell-mode) +;;; (setq mode-name "Shell") +;;; (cond ((not shell-mode-map) +;;; (setq shell-mode-map (full-copy-sparse-keymap comint-mode-map)) +;;; (define-key shell-mode-map "\M-\t" 'comint-dynamic-complete) +;;; (define-key shell-mode-map "\M-?" +;;; 'comint-dynamic-list-completions))) +;;; (use-local-map shell-mode-map) +;;; (make-local-variable 'shell-directory-stack) +;;; (setq shell-directory-stack nil) +;;; (setq comint-input-sentinel 'shell-directory-tracker) +;;; (run-hooks 'shell-mode-hook)) +;;; +;;; +;;; Note that make-comint is different from make-shell in that it +;;; doesn't have a default program argument. If you give make-shell +;;; a program name of NIL, it cleverly chooses one of explicit-shell-name, +;;; $ESHELL, $SHELL, or /bin/sh. If you give make-comint a program argument +;;; of NIL, it barfs. Adjust your code accordingly... +;;; + +;;; Do the user's customisation... + +(defvar comint-load-hook nil + "This hook is run when comint is loaded in. +This is a good place to put keybindings.") + +(run-hooks 'comint-load-hook) + +;;; Change log: +;;; 9/12/89 +;;; - Souped up the filename expansion procedures. +;;; Doc strings are much clearer and more detailed. +;;; Fixed a bug where doing a filename completion when the point +;;; was in the middle of the filename instead of at the end would lose. +;;; +;;; 2/17/90 +;;; - Souped up the command history stuff so that text inserted +;;; by comint-previous-input-matching is removed by following +;;; command history recalls. comint-next/previous-input-matching +;;; is now much more smoothly integrated w/the command history stuff. +;;; - Added comint-eol-on-send flag and comint-input-sender hook. +;;; Comint-input-sender based on code contributed by Jeff Peck +;;; (peck@sun.com). +;;; +;;; 3/13/90 ccm@cmu.cs.edu +;;; - Added comint-previous-similar-input for looking up similar inputs. +;;; - Added comint-send-and-get-output to allow snarfing input from +;;; buffer. +;;; - Added the ability to pick up a source file by positioning over +;;; a string in comint-get-source. +;;; - Added add-hook to make it a little easier for the user to use +;;; multiple hooks. +;;; +;;; 5/22/90 shivers +;;; - Moved Chris' multiplexed ipc stuff to comint-ipc.el. +;;; - Altered Chris' comint-get-source string feature. The string +;;; is only offered as a default if it names an existing file. +;;; - Changed comint-exec to directly crank up the process, instead +;;; of calling the env program. This made background.el happy. +;;; - Added new buffer-local var comint-ptyp. The problem is that +;;; the signalling functions don't work as advertised. If you are +;;; communicating via pipes, the CURRENT-GROUP arg is supposed to +;;; be ignored, but, unfortunately it seems to be the case that you +;;; must pass a NIL for this arg in the pipe case. COMINT-PTYP +;;; is a flag that tells whether the process is communicating +;;; via pipes or a pty. The comint signalling functions use it +;;; to determine the necessary CURRENT-GROUP arg value. The bug +;;; has been reported to the Gnu folks. +;;; - comint-dynamic-complete flushes the help window if you hit space +;;; after you execute it. +;;; - Added functions comint-send-string, comint-send-region and var +;;; comint-input-chunk-size. comint-send-string tries to prevent processes +;;; from hanging when you send them long strings by breaking them into +;;; chunks and allowing process output between chunks. I got the idea from +;;; Eero Simoncelli's Common Lisp package. Note that using +;;; comint-send-string means that the process buffer's contents can change +;;; during a call! If you depend on process output only happening between +;;; toplevel commands, this could be a problem. In such a case, use +;;; process-send-string instead. If this is a problem for people, I'd like +;;; to hear about it. +;;; - Added comint-proc-query as a simple mechanism for commands that +;;; want to query an inferior process and display its response. For a +;;; typical use, see lisp-show-arglist in cmulisp.el. +;;; - Added constant comint-version, which is now "2.01". +;;; +;;; 6/14/90 shivers +;;; - Had comint-update-env defined twice. Removed extra copy. Also +;;; renamed mem to be comint-mem, for modularity. The duplication +;;; was reported by Michael Meissner. +;;; 6/16/90 shivers +;;; - Emacs has two different mechanisms for maintaining the process +;;; environment, determined at compile time by the MAINTAIN-ENVIRONMENT +;;; #define. One uses the process-environment global variable, and +;;; one uses a getenv/setenv interface. comint-exec assumed the +;;; process-environment interface; it has been generalised (with +;;; comint-exec-1) to handle both cases. Pretty bogus. We could, +;;; of course, skip all this and just use the etc/env program to +;;; handle the environment tweaking, but that obscures process +;;; queries that other modules (like background.el) depend on. etc/env +;;; is also fairly bogus. This bug, and some of the fix code was +;;; reported by Dan Pierson. +;;; +;;; 9/5/90 shivers +;;; - Changed make-variable-buffer-local's to make-local-variable's. +;;; This leaves non-comint-mode buffers alone. Stephane Payrard +;;; reported the sloppy useage. +;;; - You can now go from comint-previous-similar-input to +;;; comint-previous-input with no problem. +;;; +;;; 12/21/90 shivers +;;; - Added a condition-case to comint-get-source. Bogus strings +;;; beginning with ~ were making the file-exists-p barf. +;;; - Added "=" to the set of chars recognised by file completion +;;; as constituting a filename. +;;; +;;; 1/90 shivers +;;; These changes comprise release 2.02: +;;; - Removed the kill-all-local-variables in comint-mode. This +;;; made it impossible for client modes to set things before calling +;;; comint-mode. (In particular, it messed up ilisp.el) In general, +;;; the client mode should be responsible for a k-a-l-v's. +;;; - Fixed comint-match-partial-pathname so that it works in +;;; more cases: if the filename begins at the start-of-buffer; +;;; if point is on the first char of the filename. Just a question +;;; of getting the tricky bits right. +;;; - Added a hook, comint-exec-hook that is run each time a process +;;; is cranked up. Useful for things like process-kill-without-query. +;;; +;;; These two were pointed out by tale: +;;; - Improved the doc string in comint-send-input a little bit. +;;; - Tweaked make-comint to check process status with comint-check-proc +;;; instead of equivalent inline code. +;;; +;;; - Prompt-search history commands have been commented out. I never +;;; liked them; I don't think anyone used them. +;;; - Made comint-exec-hook a local var, as it should have been. +;;; (This way, for instance, you can have cmushell procs kill-w/o-query, +;;; but let Scheme procs be default.) +;;; +;;; 7/91 Shivers +;;; - Souped up comint-read-noecho with an optional argument, STARS. +;;; Suggested by mjlx@EAGLE.CNSF.CORNELL.EDU. +;;; - Moved comint-previous-input-matching from C-c r to C-M-r. +;;; C-c bindings are reserved for the user. +;;; These bindings were done by Jim Blandy. +;;; These changes comprise version 2.03.