> < ^ Date: Wed, 15 Apr 1992 18:47:09 +0200
> ^ From: Goetz Pfeiffer <Goetz.Pfeiffer@NUIGalway.ie >
< ^ Subject: Re: emacs and GAP

stiller@blaze.cs.jhu.edu writes:

Is there an emacs mode for gap ? I just built GAP 3.1 on SPARC and run
in shell mode from within emacs but the shell echoes each line I type.
How do I stop this?

The lines are no longer printed twice if you start GAP with option
'-n'. This will disable the command line editing.

Here is, however, a first version of 'gap.el' which defines a GAP mode
for GNU Emacs. It carries some of GAP's features to Emacs, such as
command completion and the help facility. Any suggestions to improve
and enhance 'gap.el' are welcome.

Goetz Pfeiffer.

--Cut here-------------------------------------------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;A  gap.el                     GAP Filter                      Goetz Pfeiffer
;;
;A  @(#)$Id: 1.html,v 1.2 2004/04/21 15:06:03 felsch Exp $
;;
;Y  Copyright 1992,   Lehrstuhl D fuer Mathematik,    RWTH Aachen,    Germany
;;
;;  This file contains the definition of a GAP mode for GNU Emacs.  This mode
;;  is based on the shell mode of GNU Emacs.
;;
;;  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.
;;
;;  The GAP mode enables Emacs to run GAP  in a buffer.  Moreover  it enables
;;  editing GAP library files with  command completion.  Start  a GAP process
;;  in  buffer '*gap*' with  'M-x gap'.   Then 'TAB'  will  do  command  line
;;  completion just like GAP does without Emacs.  To  get help about  a topic
;;  enter '?'.  This is possible in any place of the  command line. The topic
;;  defaults  to the  symbol  around point.  Hitting  <return> on  a previous
;;  input line  in the  GAP buffer will send  that  line  again  to  the  GAP
;;  process.
;;
;;  Editing a '*.g' file in GAP mode is  possible but  somehow dangerous.  To
;;  enter GAP mode  type  'M-x gap-mode' in  the  buffer containing the '*.g'
;;  file. Both the help facility ('?') and  command completion are available.
;;  They will  refer to  the GAP process running in the buffer '*gap*'.  This
;;  may cause big confusion if the GAP process is busy with other things.
;;
;H  $Log: 1.html,v $
;H  Revision 1.2  2004/04/21 15:06:03  felsch
;H  Corrected links in the Forum Archive pages.   VF
;H
;H  Revision 1.1.1.1  2004/04/20 13:39:30  felsch
;H  The final GAP-Forum archive until 2003.
;H
;H  Revision 1.4  2003/06/12 19:20:31  gap
;H  Further update. AH
;H
;H  Revision 1.3  1997/08/15 11:18:41  gap
;H  New forum setup. AH
;H
;H  Revision 1.2  1997/04/24 15:23:28  gap
;H  These files were replaced by the versions in WWW. The content is basically the
;H  same but the formatting has been much more friendly towards the HTML-Converter.
;H  AH
;H
;H  Revision 1.1  1996/10/30 13:06:31  gap
;H  added forum archive and translation files.
;H
;H  Revision 1.1  1992/04/15  15:45:15  goetz
;H  Initial revision
;H
;;

(provide 'gap)
(require 'shell)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;V  gap-process-string
;V  gap-start-options
;V  gap-process-buffer
;V  gap-prompt-pattern
;;
(defvar gap-process-string "/usd/gap/3.1/src/gap.ds"
  "*A string to pass to the unix 'exec' function to start GAP")

(defvar gap-start-options (list "-l" "/usd/gap/3.1/lib/" "-m" "2m")
  "*The list of initial GAP options")

(defvar gap-process-buffer
"*gap*"
"The buffer normally running GAP.")

(defvar gap-prompt-pattern "\\(gap\\)?> "
"*Regexp used by Newline command in GAP mode to match prompt.")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;V  gap-key-map . . . . . . . . . . . . . . . . . . . . . . . . key bindings.
;;
;;  The 'gap-key-map' defines the key bindings in gap mode.
;;
;;  The  <return>   key    is  bound to    'gap-send',  '\t' is    bound   to
;;  'gap-complete' and the '?'  key is bound to 'gap-help'.
;;
(defvar gap-key-map nil)

(if gap-key-map nil
  (setq gap-key-map (copy-keymap shell-mode-map))
  (define-key gap-key-map "\C-m" 'gap-send)
  (define-key gap-key-map "\t" 'gap-complete)
  (define-key gap-key-map "?" 'gap-help))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;V  gap-syntax-table . . . . . . . . . . . . . . . . . . . . .  syntax table.
;;
;;  The 'gap-syntax-table' describes the syntax  of the  GAP  language.  This
;;  is used to  determine  an identifier, for   example (see 'gap-complete').
;;  Defines '#' to be the beginning of a comment and '\n' to be the end.
;;
(defvar gap-syntax-table nil
  "Syntax table used while in gap mode.")

(if gap-syntax-table ()
(setq gap-syntax-table (make-syntax-table))
(modify-syntax-entry ?# "<" gap-syntax-table)
(modify-syntax-entry ?\n ">" gap-syntax-table))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-send) . . . . . . . . . . . . . . . . . . . . . . send input to GAP.
;;
;;  'gap-send' sends the prepared input  to  GAP.  This function is  bound to
;;  the <return> key in  gap mode. It has  to decide  whether  the  new input
;;  has been  typed after the  last output or  whether a previous line  shall
;;  be repeated.  In  the latter  case it   has to  strip off  an  occasional
;;  prompt.
;;
(defun gap-send ()
  "Send input to GAP."
  (interactive "*")
  (or (get-buffer-process (current-buffer))
      (error "Current buffer has no process"))
  (end-of-line)
  (if (eobp)
      (progn
        (move-marker last-input-start
                     (process-mark (get-buffer-process (current-buffer))))
        (insert ?\n)
        (move-marker last-input-end (point)))
    (beginning-of-line)
    (re-search-forward gap-prompt-pattern
                       (save-excursion (end-of-line) (point)) t)
    (let ((copy (buffer-substring (point)
                                  (progn (forward-line 1) (point)))))
      (goto-char (point-max))
      (move-marker last-input-start (point))
      (insert copy)
      (move-marker last-input-end (point))))
  (let ((process (get-buffer-process (current-buffer))))
    (process-send-region process last-input-start last-input-end)
    (set-marker (process-mark process) (point)))
  (setq gap-send-state 'echo))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-mode) . . . . . . . . . . . . . . . . . . . . . . . Emacs' GAP mode.
;;
(defun gap-mode ()
  "Major mode for interacting with GAP and editing '*.g' files.

\\[gap] starts GAP.

\\[gap-send] sends input to GAP.

\\[gap-help] gives help on a GAP subject.

Most commands from the Emacs' shell mode are available as well.

Entry to this mode calls the value of gap-mode-hook with no args,
if that value is non-nil."

(interactive)
(kill-all-local-variables)
(setq major-mode 'gap-mode)
(setq mode-name "GAP")
(setq mode-line-process '(": %s"))
(use-local-map gap-key-map)

;; rebind RET if editing file
(or (get-buffer-process (current-buffer))
(local-set-key "\C-m" 'newline))

(set-syntax-table gap-syntax-table)
(make-local-variable 'gap-send-state)
(setq gap-send-state 'normal)
(make-local-variable 'last-input-start)
(setq last-input-start (make-marker))
(make-local-variable 'last-input-end)
(setq last-input-end (make-marker))

(run-hooks 'gap-mode-hook))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-output-filter PROC STRING) . . . . . . . . . . .  handle GAP output.
;;
;;  The  filter 'gap-output-filter' is used to process GAP output  before
;;  it is  echoed  in the  buffer.   It behaves according  to  its  state
;;  'gap-send-state'.
;;
;;  The possible states are:
;;
;;  'normal': just put the output in the buffer.
;;
;;  'echo': suppress GAP echo of the command, then return to 'normal'.
;;
;;  'completing': handle command line completion.
;;
(defun gap-output-filter (proc string)
  (cond
    ((eq gap-send-state 'normal)
     (insert string)
     (set-marker (process-mark proc) (point)))
    ((eq gap-send-state 'echo)
     (if (string-equal string "\n") (setq gap-send-state 'normal)))
    ((eq gap-send-state 'completing)
      (if (string-equal string "\C-g")
        (beep)
        (if (not (or (string-equal string " ") (string-equal string "\C-h")))
          (insert string)))
      )))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-help-filter PROC STRING) . . . . . . . . . . . . handle help output.
;;
;;  The 'gap-help-filter'  takes  care  of GAP help  output.  The output of a
;;  help request  is  put into the  '*Help*' buffer. This filter  deletes the
;;  echo of  the request. As  GAP sends its help in   pages  we have  to type
;;  <space>  at the end  of  each  page.  The  prompt indicates that help  is
;;  complete.
;;
(defun gap-help-filter (proc string)
  (let ((cbuf (current-buffer)))
    (set-buffer "*Help*")
    (goto-char (point-max))
    (insert string)
    (beginning-of-line)
    (if (looking-at "    -- <space> for more --")
        (progn
          (delete-region (point) (point-max))
          (process-send-string proc " ")))
    (if (looking-at "\\( *[\C-h]+\\)\\|\\(\\?.*[\C-m]\\)")
        (progn
          (delete-region (point) (point-max))))
    (if (looking-at "gap>")
        (progn
          (delete-region (point) (point-max))
          (set-process-filter proc 'gap-output-filter)))
    (set-buffer cbuf)))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . runs GAP.
;;
(defun gap ()
  "Run GAP, input and output via buffer *gap*."
  (interactive)
  (pop-to-buffer
   (start-gap-process "*gap*" "gap" gap-process-string gap-start-options))
  (if (not (eq major-mode 'gap-mode)) (gap-mode))
  (setq gap-process-buffer (current-buffer))
  (set-process-filter (get-buffer-process gap-process-buffer)
                      'gap-output-filter))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-complete) . . . . . . . . . . . . . . . . . . . complete identifier.
;;
;;  'gap-complete' sends  the  partial  identifier  preceding  point  to  the
;;  running  gap   process   and   asks  this  via   '\t'   to   complete the
;;  identifier.   We  have   to  handle the   gap   output  of  the  complete
;;  identifier.  Moreover we  have   to clear the GAP   input  line  via '^X'
;;  while the intended input is still prepared in the Emacs buffer.
;;
(defun gap-complete ()
  "Complete the partial identifier preceeding point."
  (interactive "*")
  (let ((process (get-buffer-process gap-process-buffer))
        sent-successfully)
    (if (not (and process (memq (process-status process) '(run stop))))
        (error "No GAP process running in buffer %s" gap-process-buffer))
    (setq gap-completion-ident (gap-ident-around-point))

;; delete partial identifier from input line
(delete-backward-char (length gap-completion-ident))

;; ask for completion and clear input line
(setq gap-send-state 'completing)
(process-send-string process (concat gap-completion-ident "\t\C-x"))
))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-ident-around-point) . . . . . . . . . . . . identifier around point.
;;
(defun gap-ident-around-point ()
 "Return the identifier around the point as a string."
 (save-excursion
   (let (beg)
     (if (not (eobp)) (forward-char 1))
     (if (not (re-search-backward "\\w\\|\\s_" nil t))
         ""
       (forward-char 1)
       (backward-sexp)
       (setq beg (point))
       (forward-sexp)
       (buffer-substring beg (point))))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (gap-help TOPIC ARG) . . . . . . . . . . . . . . . . . . . . . . .  help.
;;
;;  'gap-help'  normally is  invoked  by pressing  the  '?'  key in gap mode.
;;  The  minibuffer  is used to ask for  the help topic.  Here the identifier
;;  around point   (see 'gap-ident-around-point') is used   as a default. The
;;  chosen  topic   is  passed to  the   GAP  help facility.   The output  is
;;  displayed in the '*Help' buffer.
;;
(defun gap-help (topic arg)
  "Display GAP help about TOPIC in the *Help* buffer."
  (interactive
   (let ((enable-recursive-minibuffers t)
         (try-word (gap-ident-around-point))
         val)
     (if (string-equal try-word "gap>")
         (setq val (read-string "GAP topic: "))
       (setq val (read-string (format "GAP topic (default %s): "
                                      try-word)))
       (if (string-equal val "")
           (setq val try-word)))
     (list val current-prefix-arg)))
  (let ((process (get-buffer-process gap-process-buffer))
        sent-successfully)
    (if (not (and process (memq (process-status process) '(run stop))))
      (error "No gap process running in buffer %s" gap-process-buffer))
    (unwind-protect
        (progn
          (with-output-to-temp-buffer "*Help*"
            (print-help-return-message))
          (set-process-filter process 'gap-help-filter)
          (process-send-string process (concat "?" topic "\n"))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;F  (start-gap-process ... ) . . . . . . . . . . . . . . . . . . . start GAP.
;;
(defun start-gap-process
  (bufferid name program switches)
  (let ((buffer (get-buffer-create bufferid))
        (disp  (getenv "DISPLAY"))
        proc proc-args proc-more-args status size)
    (setq proc (get-buffer-process buffer))
    (if proc (setq status (process-status proc)))
    (save-excursion
      (set-buffer buffer)
      (if (memq status '(run stop))
          nil
        (if proc (delete-process proc))
        (message "Starting GAP...")
(setq proc (apply 'start-process name buffer
                  (concat exec-directory "env")
                  (format "TERMCAP=emacs:co#%d:tc=unknown:"
                          (screen-width))
                  "TERM=emacs"
                  "EMACS=t"
                  "-"
                  program
                  switches))
    (setq procname (process-name proc))
    (goto-char (point-max))
    (set-marker (process-mark proc) (point))
    (shell-mode)))
buffer))

(run-hooks 'gap-mode-load-hook)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;E  Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
;;
;;  Local Variables:
;;  fill-column:         77
;;  fill-prefix:         ";;  "
;;  End:
;;

> < [top]