Subject: Re: Like the DM editor? Try ce - FREE!
From: [email-masked] (Bob Palmer)
Date: Sat, 10 Aug 1996 06:16:22 GMT

In article <[email-masked]> [email-masked] (Jim Rees) writes:
>In article <[email-masked]>, "Frederick G.M. Roeber" <[email-masked]> writes:
>
>  I've had to go to emacs as well.  Of course, being a coworker of one of
>  the lemacs authors helps.
>
>I've been trying to get emacs to behave in a rational manner, and have only
>been partly successful.
>
>By "rational," I mean that new buffers should almost always appear in a new
>frame (in e19 terminology, "window" in X terminology).  Exceptions to this
>rule would be things like completion windows.  New frames should always have
>a title matching the buffer name.  There must be a command-line utility,
>like gnuclient, that will pop up a new frame with a buffer visiting the file
>whose name was given on the command line, and there must be options to
>either wait for the buffer to be killed or not.  Killing a buffer should
>remove the frame(s) that was displaying it.

The coomands that end with -other-frame open a new frame for the buffer
and window (emacs terminology). I use the following in GNU emacs-19.30.

My read key calls the emacs function find-file-read-only-other-frame
My edit key calls the emacs function find-file-other-frame

My save key calls the function
(defun save-buffer-toggle-ro ()
  (interactive)
  (if (not buffer-read-only) (save-buffer))
  (toggle-read-only))

My exit and abort keys work the same way as the DM. If you have mutliple frames
open to the same buffer exit will save the buffer, kill the selected frame, and
leave the other frames open in write mode. The abort key kills the selected
frame without saving the buffer. When you attempt to kill the last frame and its
buffer you get a message like with the DM.

My exit key calls the function
(defun save-buffer-delete-frame ()
  (interactive)
  (save-buffer)
  (let ((buffer (current-buffer)))
    (unless (cdr (find-all-frames-internal buffer))
      (kill-buffer buffer))
    (delete-frame)))

My abort key calls the function
(defun kill-buffer-delete-frame ()
  (interactive)
  (let ((buffer (current-buffer)))
    (unless (cdr (find-all-frames-internal buffer))
      (kill-buffer buffer))
    (delete-frame)))

where find-all-frames-internal returns a list of frames containing a window which
is displaying the buffer. A frame will only be in the list once, regardless of the
number of windows displaying the target buffer.
(defun find-all-frames-internal (buffer)
  (let ((frames nil))
    (walk-windows '(lambda (wind)
                     (if (eq (window-buffer wind) buffer)
                       (let ((frm (window-frame wind)))
                         (unless (memq frm frames)
                           (push frm frames)))))
                  nil t)
    frames))

My shell key opens a new frame for each shell, unless the buffer in the current frame
is *scratch* (I typically create a shell in the initial frame). Each shell buffer is
renameed so each frame is running a unique shell process. The function is
(defun run-shell ()
  (interactive)
  (if (equal (buffer-name (current-buffer) "*scratch*")
    (switch-to-buffer "*shell*")
    (switch-to-buffer-other-frame "*shell*"))
  (shell)
  (rename-buffer "Shell" t))

My mouse-3 key calls the function
(defun mouse-find-file-read-only (evt)
  (interactive "e")
  (save-excursion
    (mouse-set-point evt)
    (let* ((file-name (extract-file-name-around-point nil))
           (pathname (or (find-completed-file-name file-name)
                         (find-completed-file-name (extract-file-name-around-point t)) )))
      (if (and pathname (file-exists-p pathname))
        (find-file-read-only-other-frame pathname)
        (error "Cannot find file \"%s\"" file-name)) )))

(defun extract-file-name-around-point (simple-names)
  (let ((skip-characters (if simple-names
                           "!\-./0-9A-Z_a-z"
                           "!#-%*-9:=?-[]-"))
    (save-excursion
      (skip-chars-backwards skip-characters)
      (let ((start (point)))
        (skip-chars-forward skip-characters)
        (let* ((filename (buffer-substring start (point)))
               (last-char (aref filename (- (length filename) 1))))
          (if (memq last-char skip-at-end)
            (setq filename (substring filename 0 -1)))
          (zap-ignored-extensions filename)))))))

(defun zap-ignored-extensions (filename)
  (let ((exts completion-ignored-extensions)
        (flen (length filename)))
    (while exts
      (let* ((ext (car exts))
             (end (if (> flen (length ext)) (- flen (length ext)) nil))
             (pos (and end (string-match ext filename end))) )
        (if pos
          (progn
            (setq filename (substring filename 0 pos))
            (setq exts nil))
          (setq exts (cdr exts))) )))
  filename)

(defun find-completed-file-name (path)
  (let* ((dirpart     (file-name-directory path))
         (filepart    (file-name-nondirectory path))
         (dirpath     (expand-file-name (or dirpart "./")))
         (completions (file-name-all-completions filepart dirpath))
         (filename    (file-name-completion filepart dirpath)) )
    (cond ((eq filename t) (concat dirpath filepart))
          ((and filename
                completions
                (member filename completions))
           (concat dirpath filename))
          (completions
           (concat dirpath
                   (completing-read "Read: "
                                    (mapcar '(lambda (x) (list x 0)) completions)
                                    nil t filename)))
          (t  nil)) ))

A couple of notes on the mouse stuff:
  1. simple-names in extract-file-name-around-point : I like to use `grep -in` on a directory
     and get lines that begin: pathname:line_num line_contents....
     clicking on the pathname part does not succede with the normal delimitier set, but does
     with the simplified name delimiter set. mouse-find-file-read-only calls
     extract-file-name-around-point twice, first using the normal delimiters, then the
     simple delimitiers.
  2. zap-ignored-extensions allows me to click on the pathname of a binary file, and read
     the corresponding source file. This works by stripping the ignored extension, and
     allowing the completion mechanism to find the completed filename. eg: clicking on
     foo.hbin causes foo.lisp to be read (.hbin is an ignored extension).

I think the above examples show that you can make emacs work a lot like the DM. I also have
functions to make window scroll more like the DM, an again key, word-wrap, etc. But the ones
I use the most are like the DM's right keys, shell, and mouse-3.

-- bob
-- 
>> Bob Palmer                         |                         remlaP boB <<
[email-masked]