[comp.emacs] lisp editing commands

jackson@freyja.css.gov (Jerry Jackson) (12/17/88)

;;;Here's some code I've been using for a long time for editing lisp.  It has
;;;completely replaced the mouse for cutting and pasting.  It also has the
;;;advantage of being useful on a terminal.
;;;
;;;There are two main functions:
;;;
;;;loop-word and loop-grab -- I have them bound to C-o and M-o
;;;
;;;
;;;They work sort of like an in-place incremental search.  As you type,
;;;atoms (if you are using loop-word) or lists (loop-grab) that match what
;;;you have typed get copied to point.  As long as the current atom or list
;;;continues to match, it stays there, but as soon as it fails to match,
;;;the next matching item comes down.  If you hit <esc> at any time, you
;;;automatically go to the next item.  If you hit <space>, <lf>, <cr>, or ')'
;;;(as well as C-g :-), you leave the mode.  When working with lists
;;;in loop-grab, if you want to keep matching past a space, hit a ';' to 
;;;match the space.  
;;;
;;;Besides being useful for cut & paste, these functions make a nice 
;;;redo/fix/history mechanism for use within a lisp interpreter.  Also,
;;;you never have to type a long variable name more than once.  It is
;;;much faster than mouse action, particularly if you want to grab something
;;;that is not currently visible on the screen. 


(defun lisp-word (name)
  (let ((here (point)))
    (setq *redo-start* here)
    (setq *word-wrapped* nil)
    (setq *tried-list* nil)
    (setq *failed* nil)
    (setq *last-redo* (cons (point) (concat "\\( \\|\t\\|\n\\|(\\|)\\)" name)))
    (setq *word-prefix-length* (- (length (cdr *last-redo*))
				  (length name)))
    (setq *old-redo* (car *last-redo*))
    (re-search-backward (cdr *last-redo*) nil t)
    (if (not (eql (point) (car *last-redo*)))
	(move-text here)
      (setq *word-wrapped* t)
      (rplaca *last-redo* (point-max))
      (goto-char (car *last-redo*))
      (re-search-backward (cdr *last-redo*) nil t)
      (if (or (eql (point) *old-redo*)
	      (< (point) *redo-start*))
	  (word-redo-fail here)
	(move-text here)))))
  

(defun next-lisp-word ()
  (set-mark (point))
  (backward-sexp)
  (delete-region (point) (mark))
  (let ((here (point)))
    (goto-char (car *last-redo*))
    (re-search-backward (cdr *last-redo*) nil t)
    (while (and (memstring (get-sexp) *tried-list*)
		(not (eql (point) (car *last-redo*))))
      (rplaca *last-redo* (point))
      (re-search-backward (cdr *last-redo*) nil t))
    (if (eql (point) (car *last-redo*))
	(if *word-wrapped*
	    (word-redo-fail here)
	  (setq *word-wrapped* t)
	  (rplaca *last-redo* (point-max))
	  (goto-char here)
	  (insert "()")
	  (next-lisp-word))
      (if (and *word-wrapped* (< (point) *redo-start*))
	  (word-redo-fail here)
	(move-text here)))))


(defun move-text (position)
  (let (temp)
    (setq *old-redo* (car *last-redo*))
    (setq *last-redo* (cons (point) (cdr *last-redo*)))
    (forward-char 1)
    (mark-sexp 1)
    (setq temp (buffer-substring (mark) (point)))
    (goto-char position)
    (insert temp)
    (setq *tried-list* (cons temp *tried-list*))))


(defun word-redo-fail (position)
  (message "no more.")
  (setq *failed* t)
  (goto-char position)
  (insert (substring (cdr *last-redo*)
		     *word-prefix-length*
		     (length (cdr *last-redo*))))
  (end-of-line))

(defun get-sexp ()
  (if (>= (point) (point-max))
      ""
    (forward-char 1)
    (mark-sexp 1)
    (prog1 (buffer-substring (point) (mark))
      (backward-char 1))))

(defun memstring (x l)
  (cond ((null l) nil)
	((string-equal (car l) x) t)
	(t (memstring x (cdr l)))))

(defun loop-word ()
  (interactive)
  (let ((key (read-char)))
    (if (eq key ?\*)
	(lisp-word "\\*")
      (lisp-word (char-to-string key)))
    (while (and (not *failed*)
		(not (eq (setq key (read-char)) ?\ ))
		(not (eq key ?\n))
		(not (eq key ?\r))
		(not (eq key ?\))))
      (cond ((eq key ?\*)
	     (rplacd *last-redo* (concat (cdr *last-redo*)
					 "\\" (char-to-string key)))
	     (setq *tried-list* (cdr *tried-list*)))
	    ((eq key ?\e))
	    (t
	     (setq *last-redo* (cons *old-redo*
				    (concat (cdr *last-redo*)
					    (char-to-string key))))
	     (setq *tried-list* (cdr *tried-list*))))
      (next-lisp-word))
    (cond ((or (eq key ?\n) (eq key ?\r))
	   (funcall (key-binding (char-to-string key))))
	  ((eq key ?\ )
	   (insert " "))
	  ((eq key ?\))
	   (insert ")")
	   (backward-char 1)
	   (balance-pars)
	   (forward-char 1)))))


(defun grab-sexp-as-string ()
  (let ((here (point)))
    (backward-up-list 1)
    (set-mark (point))
    (forward-list)
    (prog1 (buffer-substring (mark) (point))
      (goto-char here))))


(defun lisp-grab (name)
  (let ((here (point)))
    (setq *redo-start* here)
    (setq *grab-wrapped* nil)
    (setq *failed* nil)
    (setq *tried-list* nil)
    (setq *last-redo* (cons (point) (concat "(" name)))
    (setq *old-redo* (car *last-redo*))
    (search-backward (cdr *last-redo*) nil t)
    (if (not (eql (point) (car *last-redo*)))
	(move-grab here)
      (setq *grab-wrapped* t)
      (rplaca *last-redo* (point-max))
      (goto-char (car *last-redo*))
      (search-backward (cdr *last-redo*) nil t)
      (if (or (eql (point) *old-redo*)
	      (< (point) *redo-start*))
	  (redo-fail here)
	(move-grab here)))))
  

(defun next-lisp-grab ()
  (set-mark (point))
  (backward-sexp)
  (delete-region (point) (mark))
  (let ((here (point)))
    (goto-char (car *last-redo*))
    (search-backward (cdr *last-redo*) nil t)
    (while (and (memstring (get-non-atomic-sexp) *tried-list*)
		(not (eql (point) (car *last-redo*))))
      (rplaca *last-redo* (point))
      (search-backward (cdr *last-redo*) nil t))
    (if (eql (point) (car *last-redo*))
	(if *grab-wrapped*
	    (redo-fail here)
	  (setq *grab-wrapped* t)
	  (rplaca *last-redo* (point-max))
	  (goto-char here)
	  (insert "()")
	  (next-lisp-grab))
      (if (and *grab-wrapped* (< (point) *redo-start*))
	  (redo-fail here)
	(move-grab here)))))



(defun move-grab (position)
  (let (temp)
    (setq *old-redo* (car *last-redo*))
    (setq *last-redo* (cons (point) (cdr *last-redo*)))
    (forward-char 1)
    (setq temp (grab-sexp-as-string))
    (goto-char position)
    (insert temp)
    (setq *tried-list* (cons temp *tried-list*))))

(defun redo-fail (position)
  (message "no more.")
  (setq *failed* t)
  (goto-char position)
  (insert (cdr *last-redo*))
  (end-of-line))


(defun get-non-atomic-sexp ()
  (mark-sexp 1)
  (buffer-substring (point) (mark)))


(defun loop-grab ()
  (interactive)
  (let (key)
    (lisp-grab (char-to-string (read-char)))
    (while (and (not *failed*)
		(not (eq (setq key (read-char)) ?\n))
		(not (eq key ?\r))
		(not (eq key ?\ ))
		(not (eq key ?\))))
      (cond ((eq key ?\;)
	     (setq *last-redo* (cons *old-redo*
				    (concat (cdr *last-redo*) " ")))
	     (setq *tried-list* (cdr *tried-list*)))
	    ((eq key ?\e))
	    (t
	     (setq *last-redo* (cons *old-redo*
				    (concat (cdr *last-redo*)
					    (char-to-string key))))
	     (setq *tried-list* (cdr *tried-list*))))
      (next-lisp-grab))
    (cond ((or (eq key ?\n) (eq key ?\r))
	   (funcall (key-binding (char-to-string key))))
	  ((eq key ?\ )
	   (insert " "))
	  ((eq key ?\))
	   (insert ")")
	   (backward-char 1)
	   (balance-pars)
	   (forward-char 1)))))



+-----------------------------------------------------------------------------+
|   Jerry Jackson                       UUCP:  seismo!esosun!jackson          |
|   Geophysics Division, MS/22          ARPA:  esosun!jackson@seismo.css.gov  |
|   SAIC                                SOUND: (619)458-4924                  |
|   10210 Campus Point Drive                                                  |
|   San Diego, CA  92121                                                      |
+-----------------------------------------------------------------------------+