Annotation of GNUtools/emacs/lisp/server.el, revision 1.1.1.1

1.1       root        1: ;; Lisp code for GNU Emacs running as server process.
                      2: ;; Copyright (C) 1986, 1987, 1990 Free Software Foundation, Inc.
                      3: ;; Author William Sommerfeld, [email protected].
                      4: ;; Changes by [email protected] and by rms.
                      5: 
                      6: ;; This file is part of GNU Emacs.
                      7: 
                      8: ;; GNU Emacs is free software; you can redistribute it and/or modify
                      9: ;; it under the terms of the GNU General Public License as published by
                     10: ;; the Free Software Foundation; either version 1, or (at your option)
                     11: ;; any later version.
                     12: 
                     13: ;; GNU Emacs is distributed in the hope that it will be useful,
                     14: ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
                     15: ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     16: ;; GNU General Public License for more details.
                     17: 
                     18: ;; You should have received a copy of the GNU General Public License
                     19: ;; along with GNU Emacs; see the file COPYING.  If not, write to
                     20: ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
                     21: 
                     22: 
                     23: ;;; This Lisp code is run in Emacs when it is to operate as
                     24: ;;; a server for other processes.
                     25: 
                     26: ;;; Load this library and do M-x server-edit to enable Emacs as a server.
                     27: ;;; Emacs runs the program ../etc/server as a subprocess
                     28: ;;; for communication with clients.  If there are no client buffers to edit, 
                     29: ;;; server-edit acts like (switch-to-buffer (other-buffer))
                     30: 
                     31: ;;; When some other program runs "the editor" to edit a file,
                     32: ;;; "the editor" can be the Emacs client program ../etc/emacsclient.
                     33: ;;; This program transmits the file names to Emacs through
                     34: ;;; the server subprocess, and Emacs visits them and lets you edit them.
                     35: 
                     36: ;;; Note that any number of clients may dispatch files to emacs to be edited.
                     37: 
                     38: ;;; When you finish editing a Server buffer, again call server-edit
                     39: ;;; to mark that buffer as done for the client and switch to the next 
                     40: ;;; Server buffer.  When all the buffers for a client have been edited 
                     41: ;;; and exited with server-edit, the client "editor" will return
                     42: ;;; to the program that invoked it.  
                     43: 
                     44: ;;; Your editing commands and Emacs's display output go to and from
                     45: ;;; the terminal in the usual way.  Thus, server operation is possible
                     46: ;;; only when Emacs can talk to the terminal at the time you invoke
                     47: ;;; the client.  This is possible in two cases:
                     48: 
                     49: ;;; 1. On a window system, where Emacs runs in one window and the
                     50: ;;; program that wants to use "the editor" runs in another.
                     51: 
                     52: ;;; 2. When the program that wants to use "the editor" is running
                     53: ;;; as a subprocess of Emacs.
                     54: 
                     55: ;;; The buffer local variable "server-buffer-clients" lists 
                     56: ;;; the clients who are waiting for this buffer to be edited.  
                     57: ;;; The global variable "server-clients" lists all the waiting clients,
                     58: ;;; and which files are yet to be edited for each.
                     59: 
                     60: (defvar server-program "server"
                     61:   "*The program to use as the edit server")
                     62: 
                     63: (defvar server-process nil 
                     64:   "the current server process")
                     65: 
                     66: (defvar server-previous-string "")
                     67: 
                     68: (defvar server-clients nil
                     69:   "List of current server clients.
                     70: Each element is (CLIENTID FILES...) where CLIENTID is a string
                     71: that can be given to the server process to identify a client.
                     72: When a buffer is marked as \"done\", it is removed from this list.")
                     73: 
                     74: (defvar server-buffer-clients nil
                     75:   "List of clientids for clients requesting editing of current buffer.")
                     76: 
                     77: (make-variable-buffer-local 'server-buffer-clients)
                     78: (setq-default server-buffer-clients nil)
                     79: (or (assq 'server-buffer-clients minor-mode-alist)
                     80:     (setq minor-mode-alist (cons '(server-buffer-clients " Server") minor-mode-alist)))
                     81: 
                     82: ;; If a *server* buffer exists,
                     83: ;; write STRING to it for logging purposes.
                     84: (defun server-log (string)
                     85:   (if (get-buffer "*server*")
                     86:       (save-excursion
                     87:        (set-buffer "*server*")
                     88:        (goto-char (point-max))
                     89:        (insert string)
                     90:        (or (bobp) (newline)))))
                     91: 
                     92: (defun server-sentinel (proc msg)
                     93:   (cond ((eq (process-status proc) 'exit)
                     94:         (server-log (message "Server subprocess exited")))
                     95:        ((eq (process-status proc) 'signal)
                     96:         (server-log (message "Server subprocess killed")))))
                     97: 
                     98: (defun server-start (&optional leave-dead)
                     99:   "Allow this Emacs process to be a server for client processes.
                    100: This starts a server communications subprocess through which
                    101: client \"editors\" can send your editing commands to this Emacs job.
                    102: To use the server, set up the program `etc/emacsclient' in the
                    103: Emacs distribution as your standard \"editor\".
                    104: 
                    105: Prefix arg means just kill any existing server communications subprocess."
                    106:   (interactive "P")
                    107:   ;; kill it dead!
                    108:   (if server-process
                    109:       (progn
                    110:        (set-process-sentinel server-process nil)
                    111:        (condition-case () (delete-process server-process) (error nil))))
                    112:   (condition-case () (delete-file "~/.emacs_server") (error nil))
                    113:   (condition-case ()
                    114:       (delete-file (format "/tmp/esrv%d-%s" (user-uid) (system-name)))
                    115:     (error nil))
                    116:   ;; If we already had a server, clear out associated status.
                    117:   (while server-clients
                    118:     (let ((buffer (nth 1 (car server-clients))))
                    119:       (server-buffer-done buffer)))
                    120:   (if leave-dead
                    121:       nil
                    122:     (if server-process
                    123:        (server-log (message "Restarting server")))
                    124:     (let ((process-connection-type nil))
                    125:       (setq server-process (start-process "server" nil server-program)))
                    126:     (set-process-sentinel server-process 'server-sentinel)
                    127:     (set-process-filter server-process 'server-process-filter)
                    128:     (process-kill-without-query server-process)))
                    129: 
                    130: ;Process a request from the server to edit some files.
                    131: ;Format of STRING is "Client: CLIENTID PATH PATH PATH... \n"
                    132: (defun server-process-filter (proc string)
                    133:   (server-log string)
                    134:   (setq string (concat server-previous-string string))
                    135:   (if (not (and (eq ?\n (aref string (1- (length string))))
                    136:                (eq 0 (string-match "Client: " string))))
                    137:       ;; If input is not complete, save it for later.
                    138:       (setq server-previous-string string)
                    139:     ;; If it is complete, process it now, and discard what was saved.
                    140:     (setq string (substring string (match-end 0)))
                    141:     (setq server-previous-string "")
                    142:     (let ((client (list (substring string 0 (string-match " " string))))
                    143:          (files nil)
                    144:          (lineno 1))
                    145:       (setq string (substring string (match-end 0)))
                    146:       (while (string-match "[^ ]+ " string)
                    147:        (let ((arg
                    148:               (substring string (match-beginning 0) (1- (match-end 0)))))
                    149:          (setq string (substring string (match-end 0)))
                    150:          (if (string-match "\\`\\+[0-9]+\\'" arg)
                    151:              (setq lineno (read (substring arg 1)))
                    152:            (setq files
                    153:                  (cons (list arg lineno)
                    154:                        files))
                    155:            (setq lineno 1))))
                    156:       (server-visit-files files client)
                    157:       ;; CLIENT is now a list (CLIENTNUM BUFFERS...)
                    158:       (setq server-clients (cons client server-clients))
                    159:       (switch-to-buffer (nth 1 client))
                    160:       (message (substitute-command-keys
                    161:                "When done with a buffer, type \\[server-edit].")))))
                    162: 
                    163: (defun server-visit-files (files client)
                    164:   "Finds FILES and returns the list CLIENT with the buffers nconc'd.
                    165: FILES is an alist whose elements are (FILENAME LINENUMBER)."
                    166:   (let (client-record)
                    167:     (while files
                    168:       (save-excursion
                    169:        ;; If there is an existing buffer modified or the file is modified,
                    170:        ;; revert it.
                    171:        ;; If there is an existing buffer with deleted file, offer to write it.
                    172:        (let* ((filen (car (car files)))
                    173:               (obuf (get-file-buffer filen)))
                    174:          (if (and obuf (set-buffer obuf))
                    175:              (if (file-exists-p filen)
                    176:                  (if (or (not (verify-visited-file-modtime obuf))
                    177:                          (buffer-modified-p obuf))
                    178:                      (revert-buffer t nil))
                    179:                (if (y-or-n-p
                    180:                     (concat "File no longer exists: "
                    181:                             filen
                    182:                             ", write buffer to file? "))
                    183:                    (write-file filen)))
                    184:            (set-buffer (find-file-noselect filen))))
                    185:        (goto-line (nth 1 (car files)))
                    186:        (setq server-buffer-clients (cons (car client) server-buffer-clients))
                    187:        (setq client-record (cons (current-buffer) client-record)))
                    188:         (setq files (cdr files)))
                    189:     (nconc client client-record)))
                    190: 
                    191: (defun server-buffer-done (buffer)
                    192:   "Mark BUFFER as \"done\" for its client(s).
                    193: Buries the buffer, and returns another server buffer
                    194: as a suggestion for what to select next."
                    195:   (let ((running (eq (process-status server-process) 'run))
                    196:        (next-buffer nil)
                    197:        (old-clients server-clients))
                    198:     (while old-clients
                    199:       (let ((client (car old-clients)))
                    200:        (or next-buffer 
                    201:            (setq next-buffer (nth 1 (memq buffer client))))
                    202:        (delq buffer client)
                    203:        ;; If client now has no pending buffers,
                    204:        ;; tell it that it is done, and forget it entirely.
                    205:        (if (cdr client) nil
                    206:          (if running
                    207:              (progn
                    208:                (send-string server-process 
                    209:                             (format "Close: %s Done\n" (car client)))
                    210:                (server-log (format "Close: %s Done\n" (car client)))))
                    211:          (setq server-clients (delq client server-clients))))
                    212:       (setq old-clients (cdr old-clients)))
                    213:     (if (buffer-name buffer)
                    214:        (save-excursion
                    215:          (set-buffer buffer)
                    216:          (setq server-buffer-clients nil)))
                    217:     (bury-buffer buffer)
                    218:     next-buffer))
                    219: 
                    220: (defun mh-draft-p (buffer)
                    221:   "Return non-nil if this BUFFER is an mh <draft> file.
                    222: Since MH deletes draft *BEFORE* it is edited, the server treats them specially."
                    223:  ;; This may not be appropriately robust for all cases.
                    224:   (string= (buffer-name buffer) "draft"))
                    225: 
                    226: (defun server-done ()
                    227:   "Offer to save current buffer, mark it as \"done\" for clients,
                    228: bury it, and return a suggested buffer to select next."
                    229:   (let ((buffer (current-buffer)))
                    230:     (if server-buffer-clients
                    231:        (progn
                    232:          (if (mh-draft-p buffer)
                    233:              (progn (save-buffer)
                    234:                     (write-region (point-min) (point-max)
                    235:                                   (concat buffer-file-name "~"))
                    236:                     (kill-buffer buffer))
                    237:            (if (and (buffer-modified-p)
                    238:                     (y-or-n-p (concat "Save file " buffer-file-name "? ")))
                    239:                (save-buffer buffer)))
                    240:          (server-buffer-done buffer)))))
                    241: 
                    242: (defun server-edit (&optional arg)
                    243:   "Switch to next server editing buffer; say \"Done\" for current buffer.
                    244: If a server buffer is current, it is marked \"done\" and optionally saved.
                    245: MH <draft> files are always saved and backed up, no questions asked.
                    246: When all of a client's buffers are marked as \"done\", the client is notified.
                    247: 
                    248: If invoked with a prefix argument, or if there is no server process running, 
                    249: starts server process and that is all.  Invoked by \\[server-edit]."
                    250:   (interactive "P")
                    251:   (if (or arg
                    252:          (not server-process)
                    253:          (memq (process-status server-process) '(signal exit)))
                    254:       (server-start nil)
                    255:     (server-switch-buffer (server-done))))
                    256: 
                    257: (defun server-switch-buffer (next-buffer)
                    258:   "Switch to another buffer, preferably one that has a client.
                    259: Arg NEXT-BUFFER is a suggestion; if it is a live buffer, use it."
                    260:   (if next-buffer
                    261:       (if (and (bufferp next-buffer)
                    262:               (buffer-name next-buffer))
                    263:          (switch-to-buffer next-buffer)
                    264:        ;; If NEXT-BUFFER is a dead buffer,
                    265:        ;; remove the server records for it
                    266:        ;; and try the next surviving server buffer.
                    267:        (server-switch-buffer
                    268:         (server-buffer-done next-buffer)))
                    269:     (if server-clients
                    270:        (server-switch-buffer (nth 1 (car server-clients)))
                    271:       (switch-to-buffer (other-buffer)))))
                    272: 
                    273: (global-set-key "\C-x#" 'server-edit)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.