gnb@bby.oz (Gregory N. Bond) (03/07/89)
[I have previously posted this to an aus distribution and got no reply] I am looking for pointers or information on the clisent/server mode of GNUemacs. I have found the client and server programs but can't work out how they are suppoed to be used, and can't find anything in the FM. My understanding is that this will allow me to use one emacs process (which is quite large) as a server and several clients as the editor for MH/rn/4GL and other "call editors within an application" uses, without continual creating/destroying emacs processes. My environment: GNU emacs 18.52, SunOs 3.5 under both SunView and yukko 80x25 terminals. Thanks for your help. Greg. -- Gregory Bond, Burdett Buckeridge & Young Ltd, Melbourne, Australia Internet: gnb@melba.bby.oz.au non-MX: gnb%melba.bby.oz@uunet.uu.net Uucp: {uunet,mnetor,pyramid,ubc-vision,ukc,mcvax,...}!munnari!melba.bby.oz!gnb
neilc@natmlab.dms.oz (Neil Crellin) (03/08/89)
In article <GNB.89Mar7151044@melba.bby.oz>, gnb@bby (Gregory N. Bond) writes: >[I have previously posted this to an aus distribution and got no reply] Sorry Greg, I saw it there but was snowed under. >I am looking for pointers or information on the client/server mode of >GNUemacs. I have found the client and server programs but can't work >out how they are suppoed to be used, and can't find anything in the FM. In etc/NEWS we find the following: (Try also C-h f server-start and look at lisp/server.el) * Existing Emacs usable as a server. Programs such as mailers that invoke "the editor" as an inferior to edit some text can now be told to use an existing Emacs process instead of creating a new editor. To do this, you must have an Emacs process running and capable of doing terminal I/O at the time you want to invoke it. This means that either you are using a window system and give Emacs a separate window or you run the other programs as inferiors of Emacs (such as, using M-x shell). First prepare the existing Emacs process by loading the `server' library and executing M-x server-start. (Your .emacs can do this automatically.) Now tell the other programs to use, as "the editor", the Emacs client program (etc/emacsclient, located in the same directory as this file). This can be done by setting the environment variable EDITOR. When another program invokes the emacsclient as "the editor", the client actually transfers the file names to be edited to the existing Emacs, which automatically visits the files. When you are done editing a buffer for a client, do C-x # (server-edit). This marks that buffer as done, and selects the next buffer that the client asked for. When all the buffers requested by a client are marked in this way, Emacs tells the client program to exit, so that the program that invoked "the editor" will resume execution. You can only have one server Emacs at a time, but multiple client programs can put in requests at the same time. The client/server work only on Berkeley Unix, since they use the Berkeley sockets mechanism for their communication. >My understanding is that this will allow me to use one emacs process >(which is quite large) as a server and several clients as the editor >for MH/rn/4GL and other "call editors within an application" uses, >without continual creating/destroying emacs processes. Sounds that way. Have fun. -- Neil Crellin, CSIRO Maths and Stats, Sydney, Australia. (neilc@natmlab.oz.au) PO Box 218, Lindfield, NSW 2070. (ph) +61 2 467 6721 (fax) +61 2 416 9317
karl@triceratops.cis.ohio-state.edu (Karl Kleinpaste) (03/08/89)
In your .emacs, put this line: (server-edit) and thereafter, you can run `emacsclient /some/file/name' to edit files. It'll open a connection to the existing emacs, tell it what file to edit, and emacs will go get that file. Edit a while, and when you're done, type C-x # which will tell the (waiting) emacsclient that you're done. It is in your best interest to setenv EDITOR /usr/local/bin/emacsclient and so forth, so that programs which invoke editors on their own will do it The Right Way. (Of course, since you're a GNUS user, this may not matter much to you.) From emacs/lisp/server.el: ;;; Load this library and do M-x server-edit to enable Emacs as a server. ;;; Emacs runs the program ../etc/server as a subprocess ;;; for communication with clients. If there are no client buffers to edit, ;;; server-edit acts like (switch-to-buffer (other-buffer)) ;;; When some other program runs "the editor" to edit a file, ;;; "the editor" can be the Emacs client program ../etc/emacsclient. ;;; This program transmits the file names to Emacs through ;;; the server subprocess, and Emacs visits them and lets you edit them. ;;; Note that any number of clients may dispatch files to emacs to be edited. ;;; When you finish editing a Server buffer, again call server-edit ;;; to mark that buffer as done for the client and switch to the next ;;; Server buffer. When all the buffers for a client have been edited ;;; and exited with server-edit, the client "editor" will return ;;; to the program that invoked it. --Karl
tale@pawl.rpi.edu (David C Lawrence) (03/08/89)
In article <KARL.89Mar8083603@triceratops.cis.ohio-state.edu> karl@triceratops.cis.ohio-state.edu (Karl Kleinpaste) writes: Karl> In your .emacs, put this line: Karl> (server-edit) That's peculiar. I have to do server-start, not -edit, it GNU Emacs 18.50.2. What version is server-edit from? Regardless, Joe Wells posted something useful a few moths ago. It seems as though there is a problem with changing the mode of a file you are editing via the server. kill-all-local-variables, called by most major modes, trashes server-buffer-clients, making the C-x # effectively disabled. In order to circumvent this problem, kill-all-local-variables was slightly modified to preserve variables which have a "preserved" property. After you start the server you should immediately execute: (put 'server-buffer-clients 'preserved t) and add have the following short segment of code loaded however you like, probably in your .emacs. Good luck with it. Dave --- Cutline --- ;; Enhancement to kill-all-local-variables, author Joe Wells ;; jbw%bucsf.bu.edu@bu-it.bu.edu (school year) ;; joew%uswest@boulder.colorado.edu (summer) ;; save the original subr function definition of kill-all-local-variables (or (fboundp 'original-kill-all-local-variables) (fset 'original-kill-all-local-variables (symbol-function 'kill-all-local-variables))) (defun kill-all-local-variables () "Eliminate all the buffer-local variable values of the current buffer. This buffer will then see the default values of all variables. NOTE: This function has been modified to ignore buffer-local variables whose preserved property is non-nil." (let ((oldvars (buffer-local-variables))) (original-kill-all-local-variables) (while oldvars (let ((var (car (car oldvars)))) (cond ((get var 'preserved) (make-local-variable var) (set var (cdr (car oldvars)))))) (setq oldvars (cdr oldvars))))) --- CUtline --- -- tale@rpitsmts.bitnet, tale%mts@rpitsgw.rpi.edu, tale@pawl.rpi.edu
gaynor@porthos.rutgers.edu (Silver) (03/11/89)
neilc@natmlab.dms.oz writes: > To do this [the emacs server/client thing], you must have an Emacs process > running and capable of doing terminal I/O at the time you want to invoke it. > This means that either you are using a window system and give Emacs a > separate window or you run the other programs as inferiors of Emacs (such as, > using M-x shell). I've had experience otherwise. server = emacs -f server-start client = .../etc/emacsclient foo = a program that invokes the value of the envariable EDITOR On Pyramids, Celerities, and Suns, I have had no problem 1. starting then suspending server 2. running foo, which in turn invokes client 3. suspending foo invoking client, and continuing server 4. completing the editing session in server 5. resuming foo invoking client 6. Done This is not a convenient sequence of steps, though... Regards, [Ag] gaynor@rutgers.edu
tale@pawl.rpi.edu (David C Lawrence) (03/11/89)
In e-mail, Peter Baer Galvin <galvin-peter@YALE.ARPA> asked the following: > This is probably a dumb question (which is why I'm mailing it to you > rather than posting it to the new group :-). I'd like to set a couple > of things automatically in each buffer created by a emacsclient > program. I'd like auto-fill mode set and a special fill-prefix too. > I tried > (setq fundamental-mode-hook 'turn-on-auto-fill) > (setq fill-prefix " ") > but the fundamental mode buffer started by emacsclient isn't in text > mode and has no fill prefix. If I get this working I'll be very > happy. Of course I'd be happied if emacsclient could talk to a remote > host (since it uses sockets anyway) so I could have one emacs on one > system and have all other editor-invocations from other systems talk > to it. Maybe in the next release, hey :-). Well, since the answer is non-obvious as near as I can tell (and not dumb at all), I'll accept the responsibility for bothering the group with it. What Peter has encountered here are two problems: a) the server provides no hook of it's own for server buffers and b) fundamental mode calls no hook (that I can find) in and of itself. Additionally, if the server visited a file in something other than findamental mode, your hook wouldn't run anyway (kind of moot, though). One way around this is to note that server-visit-files is called by the server process filter to find the buffers for client files. find-file-hooks is going to be run in it by find-file-noselect so configuring your find-file-hooks to deal with the server should do what you want. The variable client-record is bound in the let statement which makes up the body of server-visit-files, so the following should work: (setq find-file-hooks '(lambda () (if (boundp 'client-record) (progn (turn-on-auto-fill) (set-fill-prefix " "))) ;; here you should put whatever else is a ;; part of your find-file-hooks )) Through non-rigorous testing on a couple of files here that seems to do the trick. Hope it at least points you in the right direction. Dave -- tale@rpitsmts.bitnet, tale%mts@rpitsgw.rpi.edu, tale@pawl.rpi.edu
billo@cadnetix.COM (Bill Oakley) (03/13/89)
On the subject of GNUemacs server/client mode, I have a tool that invokes $EDITOR with the command: $EDITOR +<line number> <filename>. If EDITOR is set to "emacs" this works fine, but is kind of wasteful. It would be better to set EDITOR to "emacsclient", as has been discussed here. If I do this, however, I first get a buffer/new file of the name +<line number>, and when I C-# out of it I get the buffer containing <filename>, with the cursor positioned at line one. Ideally, I'd like emacs to recognize the +<line number>, save it, switch to the buffer containing <filename>, and then execute a goto-line to <line number>. As a second choice, I'd like emacs to just ignore the +<line number>. (I realize this might screw up filenames beginning with '+', but that's ok.) Anyway, I've looked at server.el a little bit, and this seems to be the place to make the changes, but before I jumped in and tried my hand at it I thought I would see if anyone has already done something like this. By the way, I'm running 18.44 (and wishing the systems people would upgrade to 18.52) on a Sun3 OS3 (soon to be OS4) machine. Thanks for your help. Bill Oakley billo@cadnetix.com Cadnetix Corporation ...!{uunet,sunpeaks,boulder}!cadnetix!billo (303) 444-8075 x489
les@chinet.chi.il.us (Leslie Mikesell) (03/13/89)
In article <5132@natmlab.dms.oz> neilc@natmlab.dms.oz (Neil Crellin) writes: >The client/server work only on Berkeley Unix, since they use the Berkeley >sockets mechanism for their communication. At least versions 18.5[12] will work under sysV with FIFOs if you define HAVE_SVIPC. You still need a windowing system but it will work (for example) on the AT&T 3b1 console windows. You can start the server in one full-screen window, suspend it and go to anther session where you invoke "emacsclient file(s). Then you jump back to the server session where the file will already be open for you. The same technique should work on other machines that have multiple virtual consoles or real windowing systems. Les Mikesell
sft@ihlpa.ihlpa (Scott Thompson) (03/14/89)
The emacs client/server mode does work on SYSV IPC as well. -- -- Scott Thompson (IHP 2A-428), AT&T Bell Labs, Naperville, Il. 60566 VOICE: (312)-416-4236 UUCP: ...!att!ihlpa!sft
scott@cdp.UUCP (03/17/89)
Is it possible to use emacs server/client mode with shl in System V Release 3? I'd like to have an emacs in one shell layer, and access it with emacsclient in the other layers. I tried to use SYSVIPC and emacsclient hung waiting to talk to emacs (if I remember correctly). I'm using 18.52 on release 3.0 for the '386. -scott {hplabs,pyramid,arisia.xerox.com}!cdp!scott
les@chinet.chi.il.us (Leslie Mikesell) (03/21/89)
In article <132500001@cdp> scott@cdp.UUCP writes: > >Is it possible to use emacs server/client mode with shl in >System V Release 3? I'd like to have an emacs in one shell >layer, and access it with emacsclient in the other layers. >I tried to use SYSVIPC and emacsclient hung waiting to >talk to emacs (if I remember correctly). I'm using 18.52 >on release 3.0 for the '386. I tried this but could not switch out of the emacs layer. Emacs seems to disable the switch character and intercept ^Z to run a non-interactive subshell. You can start an interactive shell and invoke emacsclient there, then emacs will just switch buffers and load the file in the new buffer. When you type ctl-x# you are switched back to the shell buffer and the emacsclient will exit. The IPC mechanism should work with shl if you could persuade emacs to leave the switch character alone (but then you have to pick a control character to steal from the emacs commands).
msb@ho5cad (Mike Balenger) (03/22/89)
>On the subject of GNUemacs server/client mode, I have a tool that invokes >$EDITOR with the command: > > $EDITOR +<line number> <filename>. We got the following code from the net somewhere. We have it in server_num.el. It should replace server.el. Most of the code is the same in server.el and server_num.el. Here's the section from my .gnuemacs.el: ;; ================================================================ ;; client/server set-up ;; (autoload 'server-edit "server_num" "emacs-client stuff" t) (autoload 'server-start "server_num" "emacs-client stuff" t) (global-set-key "\C-x#" 'server-edit) (server-start) It works for the above example, but if you try to do the following: $EDITOR +1 file1 +2 file2 +3 file3 you effectively get $EDITOR +3 file1 +3 file2 +3 file3 Does anyone more versed in lisp have any idea why ALL files whould be edited at the line number specified for the LAST file? I've looked through the sections of code that put the linenumbers and filenames together in the list, and the sections that take them out of the list. They look like the car's and cdr's are correct. Is the linenumber variable somehow getting declared as global, thereby getting overwritten on the last assignment? (Look in server-process-filter and server-visit-files for the modifications to accomodate linenum/filename pairs.) ---------------------------------------------------------------------- <cute quote> Michael S. Balenger (201) 949-8789 <cute disclaimer> AT&T Bell Labs Room 1L-405 msb@ho5cad.att.com Crawfords Corner Road att!ho5cad!msb Holmdel, NJ 07733 cut here ================================================================ ================================================================ ================================================================ ;; server.el (emacsclient +number filename) ;; ;; Lisp code for GNU Emacs running as server process. ;; Copyright (C) 1986, 1987 Free Software Foundation, Inc. ;; Author William Sommerfeld, wesommer@athena.mit.edu. ;; Changes by peck@sun.com and by rms. ;; This file is part of GNU Emacs. ;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY. No author or distributor ;; accepts responsibility to anyone for the consequences of using it ;; or for whether it serves any particular purpose or works at all, ;; unless he says so in writing. Refer to the GNU Emacs General Public ;; License for full details. ;; Everyone is granted permission to copy, modify and redistribute ;; GNU Emacs, but only under the conditions described in the ;; GNU Emacs General Public License. A copy of this license is ;; supposed to have been given to you along with GNU Emacs so you ;; can know your rights and responsibilities. It should be in a ;; file named COPYING. Among other things, the copyright notice ;; and this notice must be preserved on all copies. ;;; This Lisp code is run in Emacs when it is to operate as ;;; a server for other processes. ;;; Load this library and do M-x server-edit to enable Emacs as a server. ;;; Emacs runs the program ../etc/server as a subprocess ;;; for communication with clients. If there are no client buffers to edit, ;;; server-edit acts like (switch-to-buffer (other-buffer)) ;;; When some other program runs "the editor" to edit a file, ;;; "the editor" can be the Emacs client program ../etc/emacsclient. ;;; This program transmits the file names to Emacs through ;;; the server subprocess, and Emacs visits them and lets you edit them. ;;; Note that any number of clients may dispatch files to emacs to be edited. ;;; When you finish editing a Server buffer, again call server-edit ;;; to mark that buffer as done for the client and switch to the next ;;; Server buffer. When all the buffers for a client have been edited ;;; and exited with server-edit, the client "editor" will return ;;; to the program that invoked it. ;;; Your editing commands and Emacs's display output go to and from ;;; the terminal in the usual way. Thus, server operation is possible ;;; only when Emacs can talk to the terminal at the time you invoke ;;; the client. This is possible in two cases: ;;; 1. On a window system, where Emacs runs in one window and the ;;; program that wants to use "the editor" runs in another. ;;; 2. When the program that wants to use "the editor" is running ;;; as a subprocess of Emacs. ;;; The buffer local variable "server-buffer-clients" lists ;;; the clients who are waiting for this buffer to be edited. ;;; The global variable "server-clients" lists all the waiting clients, ;;; and which files are yet to be edited for each. (defvar server-program "server" "*The program to use as the edit server") (defvar server-process nil "the current server process") (defvar server-clients nil "List of current server clients. Each element is (CLIENTID FILES...) where CLIENTID is a string that can be given to the server process to identify a client. When a buffer is marked as \"done\", it is removed from this list.") (defvar server-buffer-clients nil "List of clientids for clients requesting editing of current buffer.") (make-variable-buffer-local 'server-buffer-clients) (setq-default server-buffer-clients nil) (or (assq 'server-buffer-clients minor-mode-alist) (setq minor-mode-alist (cons '(server-buffer-clients " Server") minor-mode-alist))) ;; If a *server* buffer exists, ;; write STRING to it for logging purposes. (defun server-log (string) (if (get-buffer "*server*") (save-excursion (set-buffer "*server*") (goto-char (point-max)) (insert string) (or (bobp) (newline))))) (defun server-sentinel (proc msg) (cond ((eq (process-status proc) 'exit) (server-log (message "Server subprocess exited"))) ((eq (process-status proc) 'signal) (server-log (message "Server subprocess killed"))))) (defun server-start (&optional leave-dead) "Allow this Emacs process to be a server for client processes. This starts a server communications subprocess through which client \"editors\" can send your editing commands to this Emacs job. To use the server, set up the program `etc/emacsclient' in the Emacs distribution as your standard \"editor\". Prefix arg means just kill any existing server communications subprocess." (interactive "P") ;; kill it dead! (if server-process (progn (set-process-sentinel server-process nil) (condition-case () (delete-process server-process) (error nil)))) (condition-case () (delete-file "~/.emacs_server") (error nil)) ;; If we already had a server, clear out associated status. (while server-clients (let ((buffer (nth 1 (car server-clients)))) (server-buffer-done buffer))) (if leave-dead nil (server-log (message "Restarting server")) (setq server-process (start-process "server" nil server-program)) (set-process-sentinel server-process 'server-sentinel) (set-process-filter server-process 'server-process-filter) (process-kill-without-query server-process))) ;Process a request from the server to edit some files. ;Format of STRING is "Client: CLIENTID PATH PATH PATH... \n" (defun server-process-filter (proc string) (server-log string) (if (not (eq 0 (string-match "Client: " string))) nil (setq string (substring string (match-end 0))) (let ((client (list (substring string 0 (string-match " " string)))) (filenames nil) (filename nil) (lineno nil)) (setq string (substring string (match-end 0))) (while (string-match "[^ ]+ " string) (setq filename (substring string (match-beginning 0) (1- (match-end 0)))) (if (string-match "/\\(\\+[0-9]+\\)" filename) (progn (setq lineno (string-to-int (substring filename (match-beginning 1)))) (string-match "[^ ]* \\([^ ]+\\) " string) (setq filename (substring string (match-beginning 1) (match-end 1)))) (setq lineno 1)) (setq filenames (cons (list filename lineno) filenames)) (setq string (substring string (match-end 0)))) (server-visit-files filenames client) ;; CLIENT is now a list (CLIENTNUM BUFFERS...) (setq server-clients (cons client server-clients)) (switch-to-buffer (nth 1 client)) (message (substitute-command-keys "When done with a buffer, type \\[server-edit]."))))) (defun server-visit-files (filenames client) "Finds FILES and returns the list CLIENT with the buffers nconc'd." (let (client-record) (while filenames (save-excursion ;; If there is an existing buffer that's not modified, revert it. ;; If there is an existing buffer with deleted file, offer to write it. (let* ((filename (car (car filenames))) (lineno (car (cdr (car filenames)))) (obuf (get-file-buffer filename))) (if (and obuf (set-buffer obuf)) (if (file-exists-p filename) (if (buffer-modified-p obuf) nil (revert-buffer t nil)) (if (y-or-n-p (concat "File no longer exists: " filename ", write buffer to file? ")) (write-file filename))) (set-buffer (find-file-noselect filename)))) (goto-line lineno) (setq server-buffer-clients (cons (car client) server-buffer-clients)) (setq client-record (cons (current-buffer) client-record))) (setq filenames (cdr filenames))) (nconc client client-record))) (defun server-buffer-done (buffer) "Mark BUFFER as \"done\" for its client(s). Buries the buffer, and returns another server buffer as a suggestion for what to select next." (let ((running (eq (process-status server-process) 'run)) (next-buffer nil) (old-clients server-clients)) (while old-clients (let ((client (car old-clients))) (or next-buffer (setq next-buffer (nth 1 (memq buffer client)))) (delq buffer client) ;; If client now has no pending buffers, ;; tell it that it is done, and forget it entirely. (if (cdr client) nil (if running (progn (send-string server-process (format "Close: %s Done\n" (car client))) (server-log (format "Close: %s Done\n" (car client))))) (setq server-clients (delq client server-clients)))) (setq old-clients (cdr old-clients))) (if (buffer-name buffer) (save-excursion (set-buffer buffer) (setq server-buffer-clients nil))) (bury-buffer buffer) next-buffer)) (defun mh-draft-p (buffer) "Return non-nil if this BUFFER is an mh <draft> file. Since MH deletes draft *BEFORE* it is edited, the server treats them specially." ;; This may not be appropriately robust for all cases. (string= (buffer-name buffer) "draft")) (defun server-done () "Offer to save current buffer, mark it as \"done\" for clients, bury it, and return a suggested buffer to select next." (let ((buffer (current-buffer))) (if server-buffer-clients (progn (if (mh-draft-p buffer) (progn (save-buffer) (write-region (point-min) (point-max) (concat buffer-file-name "~")) (kill-buffer buffer)) (if (and (buffer-modified-p) (y-or-n-p (concat "Save file" buffer-file-name "? "))) (save-buffer buffer))) (server-buffer-done buffer))))) (defun server-edit (&optional arg) "Switch to next server editing buffer; say \"Done\" for current buffer. If a server buffer is current, it is marked \"done\" and optionally saved. MH <draft> files are always saved and backed up, no questions asked. When all of a client's buffers are marked as \"done\", the client is notified. If invoked with a prefix argument, or if there is no server process running, starts server process and that is all. Invoked by \\[server-edit]." (interactive "P") (if (or arg (not server-process) (memq (process-status server-process) '(signal exit))) (server-start nil) (server-switch-buffer (server-done)))) (defun server-switch-buffer (next-buffer) "Switch to another buffer, preferably one that has a client. Arg NEXT-BUFFER is a suggestion; if it is a live buffer, use it." (if next-buffer (if (and (bufferp next-buffer) (buffer-name next-buffer)) (switch-to-buffer next-buffer) ;; If NEXT-BUFFER is a dead buffer, ;; remove the server records for it ;; and try the next surviving server buffer. (server-switch-buffer (server-buffer-done next-buffer))) (if server-clients (server-switch-buffer (nth 1 (car server-clients))) (switch-to-buffer (other-buffer))))) (global-set-key "\C-x#" 'server-edit) ================================================================ ================================================================ ================================================================