;;; server.el --- Lisp code for GNU Emacs running as server process
;; Copyright (C) 1986, 1987, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
-;; 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
;; Author: William Sommerfeld <wesommer@athena.mit.edu>
;; Maintainer: FSF
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; 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 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, 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
;; 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, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; The global variable "server-clients" lists all the waiting clients,
;; and which files are yet to be edited for each.
+;; Todo:
+
+;; - handle command-line-args-left.
+;; - move most of the args processing and decision making from emacsclient.c
+;; to here.
+;; - fix up handling of the client's environment (place it in the terminal?).
+
;;; Code:
(eval-when-compile (require 'cl))
(defvar server-clients nil
"List of current server clients.
-Each element is (PROC PROPERTIES...) where PROC is a process object,
-and PROPERTIES is an association list of client properties.")
+Each element is a process.")
(defvar server-buffer-clients nil
"List of client processes requesting editing of current buffer.")
(defcustom server-kill-new-buffers t
"Whether to kill buffers when done with them.
If non-nil, kill a buffer unless it already existed before editing
-it with Emacs server. If nil, kill only buffers as specified by
+it with the Emacs server. If nil, kill only buffers as specified by
`server-temp-file-regexp'.
-Please note that only buffers are killed that still have a client,
-i.e. buffers visited which \"emacsclient --no-wait\" are never killed in
+Please note that only buffers that still have a client are killed,
+i.e. buffers visited with \"emacsclient --no-wait\" are never killed in
this way."
:group 'server
:type 'boolean
(defvar server-name "server")
-(defvar server-socket-dir nil
- "The directory in which to place the server socket.
-Initialized by `server-start'.")
-
-(defun server-client (proc)
- "Return the Emacs client corresponding to PROC.
-PROC must be a process object.
-The car of the result is PROC; the cdr is an association list.
-See `server-client-get' and `server-client-set'."
- (assq proc server-clients))
-
-(defun server-client-get (client property)
- "Get the value of PROPERTY in CLIENT.
-CLIENT may be a process object, or a client returned by `server-client'.
-Return nil if CLIENT has no such property."
- (or (listp client) (setq client (server-client client)))
- (cdr (assq property (cdr client))))
-
-(defun server-client-set (client property value)
- "Set the PROPERTY to VALUE in CLIENT, and return VALUE.
-CLIENT may be a process object, or a client returned by `server-client'."
- (let (p proc)
- (if (listp client)
- (setq proc (car client))
- (setq proc client
- client (server-client client)))
- (setq p (assq property client))
- (cond
- (p (setcdr p value))
- (client (setcdr client (cons (cons property value) (cdr client))))
- (t (setq server-clients
- `((,proc (,property . ,value)) . ,server-clients))))
- value))
+(defvar server-socket-dir (format "/tmp/emacs%d" (user-uid))
+ "The directory in which to place the server socket.")
(defun server-clients-with (property value)
"Return a list of clients with PROPERTY set to VALUE."
(let (result)
- (dolist (client server-clients result)
- (when (equal value (server-client-get client property))
- (setq result (cons (car client) result))))))
+ (dolist (proc server-clients result)
+ (when (equal value (process-get proc property))
+ (push proc result)))))
(defun server-add-client (proc)
"Create a client for process PROC, if it doesn't already have one.
New clients have no properties."
- (unless (server-client proc)
- (setq server-clients (cons (cons proc nil)
- server-clients))))
-
-(defun server-getenv-from (env variable)
- "Get the value of VARIABLE in ENV.
-VARIABLE should be a string. Value is nil if VARIABLE is
-undefined in ENV. Otherwise, value is a string.
-
-ENV should be in the same format as `process-environment'."
- (let (entry result)
- (while (and env (null result))
- (setq entry (car env)
- env (cdr env))
- (if (and (> (length entry) (length variable))
- (eq ?= (aref entry (length variable)))
- (equal variable (substring entry 0 (length variable))))
- (setq result (substring entry (+ (length variable) 1)))))
- result))
+ (add-to-list 'server-clients proc))
(defmacro server-with-environment (env vars &rest body)
"Evaluate BODY with environment variables VARS set to those in ENV.
(value (make-symbol "value")))
`(let ((process-environment process-environment))
(dolist (,var ,vars)
- (let ((,value (server-getenv-from ,env ,var)))
+ (let ((,value (getenv-internal ,var ,env)))
(push (if (null ,value)
,var
(concat ,var "=" ,value))
process-environment)))
(progn ,@body))))
-(defun server-delete-client (client &optional noframe)
- "Delete CLIENT, including its buffers, terminals and frames.
+(defun server-delete-client (proc &optional noframe)
+ "Delete PROC, including its buffers, terminals and frames.
If NOFRAME is non-nil, let the frames live. (To be used from
`delete-frame-functions'.)"
- (server-log (concat "server-delete-client" (if noframe " noframe"))
- client)
+ (server-log (concat "server-delete-client" (if noframe " noframe")) proc)
;; Force a new lookup of client (prevents infinite recursion).
- (setq client (server-client
- (if (listp client) (car client) client)))
- (let ((proc (car client))
- (buffers (server-client-get client 'buffers)))
- (when client
+ (when (memq proc server-clients)
+ (let ((buffers (process-get proc 'buffers)))
;; Kill the client's buffers.
(dolist (buf buffers)
(set-frame-parameter frame 'client nil)
(delete-frame frame))))
- (setq server-clients (delq client server-clients))
+ (setq server-clients (delq proc server-clients))
;; Delete the client's tty.
- (let ((terminal (server-client-get client 'terminal)))
- (when (eq (terminal-live-p terminal) t)
+ (let ((terminal (process-get proc 'terminal)))
+ ;; Only delete the terminal if it is non-nil.
+ (when (and terminal (eq (terminal-live-p terminal) t))
(delete-terminal terminal)))
;; Delete the client's process.
- (if (eq (process-status (car client)) 'open)
- (delete-process (car client)))
+ (if (eq (process-status proc) 'open)
+ (delete-process proc))
(server-log "Deleted" proc))))
+(defvar server-log-time-function 'current-time-string
+ "Function to generate timestamps for `server-buffer'.")
+
+(defconst server-buffer " *server*"
+ "Buffer used internally by Emacs's server.
+One use is to log the I/O for debugging purposes (see `server-log'),
+the other is to provide a current buffer in which the process filter can
+safely let-bind buffer-local variables like `default-directory'.")
+
+(defvar server-log nil
+ "If non-nil, log the server's inputs and outputs in the `server-buffer'.")
+
(defun server-log (string &optional client)
- "If a *server* buffer exists, write STRING to it for logging purposes.
-If CLIENT is non-nil, add a description of it to the logged
-message."
- (when (get-buffer "*server*")
- (with-current-buffer "*server*"
+ "If `server-log' is non-nil, log STRING to `server-buffer'.
+If CLIENT is non-nil, add a description of it to the logged message."
+ (when server-log
+ (with-current-buffer (get-buffer-create server-buffer)
(goto-char (point-max))
- (insert (current-time-string)
+ (insert (funcall server-log-time-function)
(cond
- ((null client) " ")
- ((listp client) (format " %s: " (car client)))
- (t (format " %s: " client)))
+ ((null client) " ")
+ ((listp client) (format " %s: " (car client)))
+ (t (format " %s: " client)))
string)
(or (bolp) (newline)))))
(defun server-select-display (display)
;; If the current frame is on `display' we're all set.
- (unless (equal (frame-parameter (selected-frame) 'display) display)
+ ;; Similarly if we are unable to open a frames on other displays, there's
+ ;; nothing more we can do.
+ (unless (or (not (fboundp 'make-frame-on-display))
+ (equal (frame-parameter (selected-frame) 'display) display))
;; Otherwise, look for an existing frame there and select it.
(dolist (frame (frame-list))
(when (equal (frame-parameter frame 'display) display)
display
;; Make it display (and remember) some dummy buffer, so
;; we can detect later if the frame is in use or not.
- `((server-dummmy-buffer . ,buffer)
+ `((server-dummy-buffer . ,buffer)
;; This frame may be deleted later (see
;; server-unselect-display) so we want it to be as
;; unobtrusive as possible.
(server-log (format "server-handle-suspend-tty, terminal %s" terminal) proc)
(condition-case err
(server-send-string proc "-suspend \n")
- (file-error (condition-case nil (server-delete-client proc) (error nil))))))
+ (file-error ;The pipe/socket was closed.
+ (ignore-errors (server-delete-client proc))))))
(defun server-unquote-arg (arg)
"Remove &-quotation from ARG.
(not server-clients)
(yes-or-no-p
"The current server still has clients; delete them? "))
- ;; It is safe to get the user id now.
- (setq server-socket-dir (or server-socket-dir
- (format "/tmp/emacs%d" (user-uid))))
(when server-process
;; kill it dead!
(ignore-errors (delete-process server-process)))
(add-hook 'delete-frame-functions 'server-handle-delete-frame)
(add-hook 'kill-buffer-query-functions 'server-kill-buffer-query-function)
(add-hook 'kill-emacs-query-functions 'server-kill-emacs-query-function)
+ (add-hook 'kill-emacs-hook (lambda () (server-mode -1))) ;Cleanup upon exit.
(setq server-process
(apply #'make-network-process
:name server-name
:filter 'server-process-filter
;; We must receive file names without being decoded.
;; Those are decoded by server-process-filter according
- ;; to file-name-coding-system.
- :coding 'raw-text
- ;; The rest of the args depends on the kind of socket used.
+ ;; to file-name-coding-system. Also don't get
+ ;; confused by CRs since we don't quote them.
+ :coding 'raw-text-unix
+ ;; The other args depend on the kind of socket used.
(if server-use-tcp
(list :family nil
:service t
(when server-use-tcp
(let ((auth-key
(loop
- ;; The auth key is a 64-byte string of random chars in the
- ;; range `!'..`~'.
- for i below 64
- collect (+ 33 (random 94)) into auth
- finally return (concat auth))))
+ ;; The auth key is a 64-byte string of random chars in the
+ ;; range `!'..`~'.
+ for i below 64
+ collect (+ 33 (random 94)) into auth
+ finally return (concat auth))))
(process-put server-process :auth-key auth-key)
(with-temp-file server-file
(set-buffer-multibyte nil)
" " (int-to-string (emacs-pid))
"\n" auth-key)))))))))
+(defun server-running-p (&optional name)
+ "Test whether server NAME is running."
+ (interactive
+ (list (if current-prefix-arg
+ (read-string "Server name: " nil nil server-name))))
+ (unless name (setq name server-name))
+ (condition-case nil
+ (progn
+ (delete-process
+ (make-network-process
+ :name "server-client-test" :family 'local :server nil :noquery t
+ :service (expand-file-name name server-socket-dir)))
+ t)
+ (file-error nil)))
+
;;;###autoload
(define-minor-mode server-mode
"Toggle Server mode.
(server-quote-arg text)))))))))
(defun server-create-tty-frame (tty type proc)
+ (add-to-list 'frame-inherited-parameters 'client)
(let ((frame
(server-with-environment (process-get proc 'env)
'("LANG" "LC_CTYPE" "LC_ALL"
"BAUDRATE" "COLUMNS" "ESCDELAY" "HOME" "LINES"
"NCURSES_ASSUMED_COLORS" "NCURSES_NO_PADDING"
"NCURSES_NO_SETBUF" "TERM" "TERMCAP" "TERMINFO"
- "TERMINFO_DIRS" "TERMPATH"
+ "TERMINFO_DIRS" "TERMPATH"
;; rxvt wants these
"COLORFGBG" "COLORTERM")
(make-frame-on-tty tty type
;; Ignore nowait here; we always need to
;; clean up opened ttys when the client dies.
`((client . ,proc)
- (environment . ,(process-get proc 'env))))))
- (client (server-client proc)))
-
- (set-frame-parameter frame 'display-environment-variable
- (server-getenv-from (process-get proc 'env) "DISPLAY"))
+ ;; This is a leftover from an earlier
+ ;; attempt at making it possible for process
+ ;; run in the server process to use the
+ ;; environment of the client process.
+ ;; It has no effect now and to make it work
+ ;; we'd need to decide how to make
+ ;; process-environment interact with client
+ ;; envvars, and then to change the
+ ;; C functions `child_setup' and
+ ;; `getenv_internal' accordingly.
+ (environment . ,(process-get proc 'env)))))))
+
+ ;; ttys don't use the `display' parameter, but callproc.c does to set
+ ;; the DISPLAY environment on subprocesses.
+ (set-frame-parameter frame 'display
+ (getenv-internal "DISPLAY" (process-get proc 'env)))
(select-frame frame)
- (server-client-set client 'frame frame)
- (server-client-set client 'tty (terminal-name frame))
- (server-client-set client 'terminal (frame-terminal frame))
+ (process-put proc 'frame frame)
+ (process-put proc 'terminal (frame-terminal frame))
;; Display *scratch* by default.
(switch-to-buffer (get-buffer-create "*scratch*") 'norecord)
frame))
(defun server-create-window-system-frame (display nowait proc)
- (if (not (fboundp 'x-create-frame))
+ (add-to-list 'frame-inherited-parameters 'client)
+ (if (not (fboundp 'make-frame-on-display))
(progn
;; This emacs does not support X.
(server-log "Window system unsupported" proc)
;; `server-save-buffers-kill-terminal' from unexpectedly
;; killing emacs on that frame.
(let* ((params `((client . ,(if nowait 'nowait proc))
+ ;; This is a leftover, see above.
(environment . ,(process-get proc 'env))))
(frame (make-frame-on-display
(or display
(frame-parameter nil 'display)
(getenv "DISPLAY")
(error "Please specify display"))
- params))
- (client (server-client proc)))
+ params)))
(server-log (format "%s created" frame) proc)
- ;; XXX We need to ensure the parameters are
- ;; really set because Emacs forgets unhandled
- ;; initialization parameters for X frames at
- ;; the moment.
- (modify-frame-parameters frame params)
- (set-frame-parameter frame 'display-environment-variable
- (server-getenv-from (process-get proc 'env) "DISPLAY"))
(select-frame frame)
- (server-client-set client 'frame frame)
- (server-client-set client 'terminal (frame-terminal frame))
+ (process-put proc 'frame frame)
+ (process-put proc 'terminal (frame-terminal frame))
;; Display *scratch* by default.
(switch-to-buffer (get-buffer-create "*scratch*") 'norecord)
on this tty until it gets a -resume command.
`-resume'
- Resume this tty frame. The client sends this string when it
+ Resume this tty frame. The client sends this string when it
gets the SIGCONT signal and it is the foreground process on its
controlling tty.
used to forward window change signals to it.
`-window-system-unsupported'
- Signals that the server does not
- support creating X frames; the client must try again with a tty
- frame.
+ Signals that the server does not support creating X frames;
+ the client must try again with a tty frame.
`-print STRING'
Print STRING on stdout. Used to send values
Signal an error (but continue processing).
`-suspend'
- Suspend this terminal, i.e., stop the client process. Sent
- when the user presses C-z."
+ Suspend this terminal, i.e., stop the client process.
+ Sent when the user presses C-z."
(server-log (concat "Received " string) proc)
;; First things first: let's check the authentication
(unless (process-get proc :authenticated)
- (if (and (string-match "-auth \\(.*?\\)\n" string)
+ (if (and (string-match "-auth \\([!-~]+\\)\n?" string)
(equal (match-string 1 string) (process-get proc :auth-key)))
(progn
(setq string (substring string (match-end 0)))
;; Save for later any partial line that remains.
(when (> (length string) 0)
(process-put proc 'previous-string string))
-
+
;; In earlier versions of server.el (where we used an `emacsserver'
;; process), there could be multiple lines. Nowadays this is not
;; supported any more.
(coding-system (and default-enable-multibyte-characters
(or file-name-coding-system
default-file-name-coding-system)))
- (client (server-client proc))
nowait ; t if emacsclient does not want to wait for us.
frame ; The frame that was opened for the client (if any).
display ; Open the frame on this display.
(tty-name nil) ;nil, `window-system', or the tty name.
tty-type ;string.
(files nil)
- (lineno 1)
- (columnno 0))
+ (filepos nil)
+ command-line-args-left
+ arg)
;; Remove this line from STRING.
(setq string (substring string (match-end 0)))
- (while (string-match " *[^ ]* " request)
- (let ((arg (substring request (match-beginning 0)
- (1- (match-end 0)))))
- (setq request (substring request (match-end 0)))
+ (setq command-line-args-left
+ (mapcar 'server-unquote-arg (split-string request " " t)))
+ (while (setq arg (pop command-line-args-left))
(cond
;; -version CLIENT-VERSION: obsolete at birth.
- ((and (equal "-version" arg) (string-match "[^ ]+ " request))
- (setq request (substring request (match-end 0))))
+ ((and (equal "-version" arg) command-line-args-left)
+ (pop command-line-args-left))
;; -nowait: Emacsclient won't wait for a result.
((equal "-nowait" arg) (setq nowait t))
;; -display DISPLAY:
;; Open X frames on the given display instead of the default.
- ((and (equal "-display" arg)
- (string-match "\\([^ ]*\\) " request))
- (setq display (match-string 1 request)
- request (substring request (match-end 0))))
+ ((and (equal "-display" arg) command-line-args-left)
+ (setq display (pop command-line-args-left))
+ (if (zerop (length display)) (setq display nil)))
;; -window-system: Open a new X frame.
((equal "-window-system" arg)
;; -resume: Resume a suspended tty frame.
((equal "-resume" arg)
- (lexical-let ((terminal (server-client-get client 'terminal)))
+ (lexical-let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
(when (eq (terminal-live-p terminal) t)
;; get out of sync, and a C-z sends a SIGTSTP to
;; emacsclient.)
((equal "-suspend" arg)
- (lexical-let ((terminal (server-client-get client 'terminal)))
+ (lexical-let ((terminal (process-get proc 'terminal)))
(setq dontkill t)
(push (lambda ()
(when (eq (terminal-live-p terminal) t)
;; -ignore COMMENT: Noop; useful for debugging emacsclient.
;; (The given comment appears in the server log.)
- ((and (equal "-ignore" arg) (string-match "[^ ]* " request))
- (setq dontkill t
- request (substring request (match-end 0))))
+ ((and (equal "-ignore" arg) command-line-args-left
+ (setq dontkill t)
+ (pop command-line-args-left)))
;; -tty DEVICE-NAME TYPE: Open a new tty frame at the client.
((and (equal "-tty" arg)
- (string-match "\\([^ ]*\\) \\([^ ]*\\) " request))
- (setq tty-name (match-string 1 request))
- (setq tty-type (match-string 2 request))
- (setq dontkill t)
- (setq request (substring request (match-end 0))))
+ (cdr command-line-args-left))
+ (setq tty-name (pop command-line-args-left)
+ tty-type (pop command-line-args-left)
+ dontkill t))
;; -position LINE[:COLUMN]: Set point to the given
;; position in the next file.
((and (equal "-position" arg)
- (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)? "
- request))
- (setq lineno (string-to-number (match-string 1 request))
- columnno (if (null (match-end 2)) 0
- (string-to-number (match-string 2 request)))
- request (substring request (match-end 0))))
+ command-line-args-left
+ (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?"
+ (car command-line-args-left)))
+ (setq arg (pop command-line-args-left))
+ (setq filepos
+ (cons (string-to-number (match-string 1 arg))
+ (string-to-number (or (match-string 2 arg) "")))))
;; -file FILENAME: Load the given file.
((and (equal "-file" arg)
- (string-match "\\([^ ]+\\) " request))
- (let ((file (server-unquote-arg (match-string 1 request))))
- (setq request (substring request (match-end 0)))
+ command-line-args-left)
+ (let ((file (pop command-line-args-left)))
(if coding-system
(setq file (decode-coding-string file coding-system)))
(setq file (command-line-normalize-file-name file))
- (push (list file lineno columnno) files)
- (server-log (format "New file: %s (%d:%d)"
- file lineno columnno) proc))
- (setq lineno 1
- columnno 0))
+ (push (cons file filepos) files)
+ (server-log (format "New file: %s %s"
+ file (or filepos "")) proc))
+ (setq filepos nil))
;; -eval EXPR: Evaluate a Lisp expression.
((and (equal "-eval" arg)
- (string-match "\\([^ ]+\\) " request))
- (lexical-let ((expr (server-unquote-arg
- (match-string 1 request))))
- (setq request (substring request (match-end 0)))
+ command-line-args-left)
+ (lexical-let ((expr (pop command-line-args-left)))
(if coding-system
(setq expr (decode-coding-string expr coding-system)))
(push (lambda () (server-eval-and-print expr proc))
commands)
- (setq lineno 1
- columnno 0)))
+ (setq filepos nil)))
;; -env NAME=VALUE: An environment variable.
- ((and (equal "-env" arg) (string-match "\\([^ ]+\\) " request))
- (let ((var (server-unquote-arg (match-string 1 request))))
+ ((and (equal "-env" arg) command-line-args-left)
+ (let ((var (pop command-line-args-left)))
;; XXX Variables should be encoded as in getenv/setenv.
- (setq request (substring request (match-end 0)))
(process-put proc 'env
(cons var (process-get proc 'env)))))
;; -dir DIRNAME: The cwd of the emacsclient process.
- ((and (equal "-dir" arg) (string-match "\\([^ ]+\\) " request))
- (setq dir (server-unquote-arg (match-string 1 request)))
- (setq request (substring request (match-end 0)))
+ ((and (equal "-dir" arg) command-line-args-left)
+ (setq dir (pop command-line-args-left))
(if coding-system
(setq dir (decode-coding-string dir coding-system)))
(setq dir (command-line-normalize-file-name dir)))
;; Unknown command.
- (t (error "Unknown command: %s" arg)))))
-
+ (t (error "Unknown command: %s" arg))))
+
(setq frame
(case tty-name
((nil) (if display (server-select-display display)))
(server-create-window-system-frame display nowait proc))
(t (server-create-tty-frame tty-name tty-type proc))))
- (process-put proc 'continuation
- (lexical-let ((proc proc)
- (files files)
- (nowait nowait)
- (commands commands)
- (dontkill dontkill)
- (frame frame)
- (tty-name tty-name))
- (lambda ()
- (server-execute proc files nowait commands
- dontkill frame tty-name))))
+ (process-put
+ proc 'continuation
+ (lexical-let ((proc proc)
+ (files files)
+ (nowait nowait)
+ (commands commands)
+ (dontkill dontkill)
+ (frame frame)
+ (dir dir)
+ (tty-name tty-name))
+ (lambda ()
+ (with-current-buffer (get-buffer-create server-buffer)
+ ;; Use the same cwd as the emacsclient, if possible, so
+ ;; relative file names work correctly, even in `eval'.
+ (let ((default-directory
+ (if (and dir (file-directory-p dir))
+ dir default-directory)))
+ (server-execute proc files nowait commands
+ dontkill frame tty-name))))))
(when (or frame files)
(server-goto-toplevel proc))
(defun server-execute (proc files nowait commands dontkill frame tty-name)
(condition-case err
- (let* ((client (server-client proc))
- (buffers
+ (let* ((buffers
(when files
(run-hooks 'pre-command-hook)
- (prog1 (server-visit-files files client nowait)
+ (prog1 (server-visit-files files proc nowait)
(run-hooks 'post-command-hook)))))
(mapc 'funcall (nreverse commands))
-
+
;; Delete the client if necessary.
(cond
(nowait
(server-log (error-message-string err) proc)
(delete-process proc)))
-(defun server-goto-line-column (file-line-col)
- "Move point to the position indicated in FILE-LINE-COL.
-FILE-LINE-COL should be a three-element list as described in
-`server-visit-files'."
- (goto-line (nth 1 file-line-col))
- (let ((column-number (nth 2 file-line-col)))
- (if (> column-number 0)
- (move-to-column (1- column-number)))))
+(defun server-goto-line-column (line-col)
+ "Move point to the position indicated in LINE-COL.
+LINE-COL should be a pair (LINE . COL)."
+ (when line-col
+ (goto-line (car line-col))
+ (let ((column-number (cdr line-col)))
+ (when (> column-number 0)
+ (move-to-column (1- column-number))))))
-(defun server-visit-files (files client &optional nowait)
+(defun server-visit-files (files proc &optional nowait)
"Find FILES and return a list of buffers created.
-FILES is an alist whose elements are (FILENAME LINENUMBER COLUMNNUMBER).
-CLIENT is the client that requested this operation.
+FILES is an alist whose elements are (FILENAME . FILEPOS)
+where FILEPOS can be nil or a pair (LINENUMBER . COLUMNNUMBER).
+PROC is the client that requested this operation.
NOWAIT non-nil means this client is not waiting for the results,
so don't mark these buffers specially, just visit them normally."
;; Bind last-nonmenu-event to force use of keyboard, not mouse, for queries.
(filen (car file))
(obuf (get-file-buffer filen)))
(add-to-history 'file-name-history filen)
- (if (and obuf (set-buffer obuf))
- (progn
- (cond ((file-exists-p filen)
- (when (not (verify-visited-file-modtime obuf))
- (revert-buffer t nil)))
- (t
- (when (y-or-n-p
- (concat "File no longer exists: " filen
- ", write buffer to file? "))
- (write-file filen))))
- (unless server-buffer-clients
- (setq server-existing-buffer t))
- (server-goto-line-column file))
- (set-buffer (find-file-noselect filen))
- (server-goto-line-column file)
- (run-hooks 'server-visit-hook)))
+ (if (null obuf)
+ (set-buffer (find-file-noselect filen))
+ (set-buffer obuf)
+ (cond ((file-exists-p filen)
+ (when (not (verify-visited-file-modtime obuf))
+ (revert-buffer t nil)))
+ (t
+ (when (y-or-n-p
+ (concat "File no longer exists: " filen
+ ", write buffer to file? "))
+ (write-file filen))))
+ (unless server-buffer-clients
+ (setq server-existing-buffer t)))
+ (server-goto-line-column (cdr file))
+ (run-hooks 'server-visit-hook))
(unless nowait
;; When the buffer is killed, inform the clients.
(add-hook 'kill-buffer-hook 'server-kill-buffer nil t)
- (push (car client) server-buffer-clients))
+ (push proc server-buffer-clients))
(push (current-buffer) client-record)))
(unless nowait
- (server-client-set
- client 'buffers
- (nconc (server-client-get client 'buffers) client-record)))
+ (process-put proc 'buffers
+ (nconc (process-get proc 'buffers) client-record)))
client-record))
\f
(defun server-buffer-done (buffer &optional for-killing)
FOR-KILLING if non-nil indicates that we are called from `kill-buffer'."
(let ((next-buffer nil)
(killed nil))
- (dolist (client server-clients)
- (let ((buffers (server-client-get client 'buffers)))
+ (dolist (proc server-clients)
+ (let ((buffers (process-get proc 'buffers)))
(or next-buffer
(setq next-buffer (nth 1 (memq buffer buffers))))
(when buffers ; Ignore bufferless clients.
(setq buffers (delq buffer buffers))
- ;; Delete all dead buffers from CLIENT.
+ ;; Delete all dead buffers from PROC.
(dolist (b buffers)
(and (bufferp b)
(not (buffer-live-p b))
(setq buffers (delq b buffers))))
- (server-client-set client 'buffers buffers)
+ (process-put proc 'buffers buffers)
;; If client now has no pending buffers,
;; tell it that it is done, and forget it entirely.
(unless buffers
- (server-log "Close" client)
- (server-delete-client client)))))
+ (server-log "Close" proc)
+ (server-delete-client proc)))))
(when (and (bufferp buffer) (buffer-name buffer))
;; We may or may not kill this buffer;
;; if we do, do not call server-buffer-done recursively
(or (not server-buffer-clients)
(let ((res t))
(dolist (proc server-buffer-clients res)
- (let ((client (server-client proc)))
- (when (and client (eq (process-status proc) 'open))
- (setq res nil)))))
+ (when (and (memq proc server-clients)
+ (eq (process-status proc) 'open))
+ (setq res nil))))
(yes-or-no-p (format "Buffer `%s' still has clients; kill it? "
(buffer-name (current-buffer))))))
(defun server-kill-emacs-query-function ()
- "Ask before exiting Emacs it has live clients."
+ "Ask before exiting Emacs if it has live clients."
(or (not server-clients)
(let (live-client)
- (dolist (client server-clients live-client)
- (when (memq t (mapcar 'buffer-live-p (server-client-get
- client 'buffers)))
+ (dolist (proc server-clients live-client)
+ (when (memq t (mapcar 'buffer-live-p (process-get
+ proc 'buffers)))
(setq live-client t))))
(yes-or-no-p "This Emacs session has clients; exit anyway? ")))
(progn
(let ((rest server-clients))
(while (and rest (not next-buffer))
- (let ((client (car rest)))
- ;; Only look at frameless clients.
- (when (not (server-client-get client 'frame))
- (setq next-buffer (car (server-client-get client 'buffers))))
+ (let ((proc (car rest)))
+ ;; Only look at frameless clients, or those in the selected
+ ;; frame.
+ (when (or (not (process-get proc 'frame))
+ (eq (process-get proc 'frame) (selected-frame)))
+ (setq next-buffer (car (process-get proc 'buffers))))
(setq rest (cdr rest)))))
(and next-buffer (server-switch-buffer next-buffer killed-one))
(unless (or next-buffer killed-one (window-dedicated-p (selected-window)))
;;;###autoload
(defun server-save-buffers-kill-terminal (proc &optional arg)
+ ;; Called from save-buffers-kill-terminal in files.el.
"Offer to save each buffer, then kill PROC.
With prefix arg, silently save all file-visiting buffers, then kill.
If emacsclient was started with a list of filenames to edit, then
only these files will be asked to be saved."
- (let ((buffers (server-client-get proc 'buffers)))
- ;; If client is bufferless, emulate a normal Emacs session
- ;; exit and offer to save all buffers. Otherwise, offer to
- ;; save only the buffers belonging to the client.
- (save-some-buffers arg
- (if buffers
- (lambda () (memq (current-buffer) buffers))
- t))
- (server-delete-client proc)))
+ ;; save-buffers-kill-terminal occasionally calls us with proc set
+ ;; to `nowait' (comes from the value of the `client' frame parameter).
+ (when (processp proc)
+ (let ((buffers (process-get proc 'buffers)))
+ ;; If client is bufferless, emulate a normal Emacs session
+ ;; exit and offer to save all buffers. Otherwise, offer to
+ ;; save only the buffers belonging to the client.
+ (save-some-buffers arg
+ (if buffers
+ (lambda () (memq (current-buffer) buffers))
+ t))
+ (server-delete-client proc))))
(define-key ctl-x-map "#" 'server-edit)
-(defun server-unload-hook ()
+(defun server-unload-function ()
"Unload the server library."
(server-mode -1)
- (remove-hook 'suspend-tty-functions 'server-handle-suspend-tty)
- (remove-hook 'delete-frame-functions 'server-handle-delete-frame)
- (remove-hook 'kill-buffer-query-functions 'server-kill-buffer-query-function)
- (remove-hook 'kill-emacs-query-functions 'server-kill-emacs-query-function)
- (remove-hook 'kill-buffer-hook 'server-kill-buffer))
-
-(add-hook 'kill-emacs-hook (lambda () (server-mode -1))) ;Cleanup upon exit.
-(add-hook 'server-unload-hook 'server-unload-hook)
+ (substitute-key-definition 'server-edit nil ctl-x-map)
+ (save-current-buffer
+ (dolist (buffer (buffer-list))
+ (set-buffer buffer)
+ (remove-hook 'kill-buffer-hook 'server-kill-buffer t)))
+ ;; continue standard unloading
+ nil)
+
\f
(provide 'server)