diff options
| author | Igor Kuzmin | 2011-02-10 13:53:49 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2011-02-10 13:53:49 -0500 |
| commit | 94d11cb5773b3b37367ee3c4885a374ff129d475 (patch) | |
| tree | b7acbbd87cfce602ad52c23f4434a3b27eac83e1 | |
| parent | 8f1d2ef658f95549eb33fe5265f8f11c5129bece (diff) | |
| download | emacs-94d11cb5773b3b37367ee3c4885a374ff129d475.tar.gz emacs-94d11cb5773b3b37367ee3c4885a374ff129d475.zip | |
* lisp/emacs-lisp/cconv.el: New file.
* lisp/emacs-lisp/bytecomp.el: Use cconv.
(byte-compile-file-form, byte-compile):
Call cconv-closure-convert-toplevel when requested.
* lisp/server.el:
* lisp/mpc.el:
* lisp/emacs-lisp/pcase.el:
* lisp/doc-view.el:
* lisp/dired.el: Use lexical-binding.
| -rw-r--r-- | lisp/ChangeLog | 12 | ||||
| -rw-r--r-- | lisp/dired.el | 1 | ||||
| -rw-r--r-- | lisp/doc-view.el | 41 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 11 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cconv.el | 891 | ||||
| -rw-r--r-- | lisp/emacs-lisp/pcase.el | 18 | ||||
| -rw-r--r-- | lisp/mpc.el | 33 | ||||
| -rw-r--r-- | lisp/server.el | 344 |
8 files changed, 1121 insertions, 230 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 7e3982a5a70..c137860013b 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,15 @@ | |||
| 1 | 2011-02-10 Igor Kuzmin <kuzminig@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * emacs-lisp/cconv.el: New file. | ||
| 4 | * emacs-lisp/bytecomp.el: Use cconv. | ||
| 5 | (byte-compile-file-form, byte-compile): | ||
| 6 | Call cconv-closure-convert-toplevel when requested. | ||
| 7 | * server.el: | ||
| 8 | * mpc.el: | ||
| 9 | * emacs-lisp/pcase.el: | ||
| 10 | * doc-view.el: | ||
| 11 | * dired.el: Use lexical-binding. | ||
| 12 | |||
| 1 | 2010-12-27 Stefan Monnier <monnier@iro.umontreal.ca> | 13 | 2010-12-27 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 14 | ||
| 3 | * emacs-lisp/bytecomp.el (byte-compile-track-mouse): Don't use #'. | 15 | * emacs-lisp/bytecomp.el (byte-compile-track-mouse): Don't use #'. |
diff --git a/lisp/dired.el b/lisp/dired.el index 02d855a0d33..f98ad641fe3 100644 --- a/lisp/dired.el +++ b/lisp/dired.el | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 1 | ;;; dired.el --- directory-browsing commands | 2 | ;;; dired.el --- directory-browsing commands |
| 2 | 3 | ||
| 3 | ;; Copyright (C) 1985-1986, 1992-1997, 2000-2011 | 4 | ;; Copyright (C) 1985-1986, 1992-1997, 2000-2011 |
diff --git a/lisp/doc-view.el b/lisp/doc-view.el index c67205fd52b..4f8c338409b 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 1 | ;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs | 2 | ;;; doc-view.el --- View PDF/PostScript/DVI files in Emacs |
| 2 | 3 | ||
| 3 | ;; Copyright (C) 2007-2011 Free Software Foundation, Inc. | 4 | ;; Copyright (C) 2007-2011 Free Software Foundation, Inc. |
| @@ -155,7 +156,7 @@ | |||
| 155 | 156 | ||
| 156 | (defcustom doc-view-ghostscript-options | 157 | (defcustom doc-view-ghostscript-options |
| 157 | '("-dSAFER" ;; Avoid security problems when rendering files from untrusted | 158 | '("-dSAFER" ;; Avoid security problems when rendering files from untrusted |
| 158 | ;; sources. | 159 | ;; sources. |
| 159 | "-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4" | 160 | "-dNOPAUSE" "-sDEVICE=png16m" "-dTextAlphaBits=4" |
| 160 | "-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET") | 161 | "-dBATCH" "-dGraphicsAlphaBits=4" "-dQUIET") |
| 161 | "A list of options to give to ghostscript." | 162 | "A list of options to give to ghostscript." |
| @@ -442,9 +443,7 @@ Can be `dvi', `pdf', or `ps'.") | |||
| 442 | doc-view-current-converter-processes) | 443 | doc-view-current-converter-processes) |
| 443 | ;; The PNG file hasn't been generated yet. | 444 | ;; The PNG file hasn't been generated yet. |
| 444 | (doc-view-pdf->png-1 doc-view-buffer-file-name file page | 445 | (doc-view-pdf->png-1 doc-view-buffer-file-name file page |
| 445 | (lexical-let ((page page) | 446 | (let ((win (selected-window))) |
| 446 | (win (selected-window)) | ||
| 447 | (file file)) | ||
| 448 | (lambda () | 447 | (lambda () |
| 449 | (and (eq (current-buffer) (window-buffer win)) | 448 | (and (eq (current-buffer) (window-buffer win)) |
| 450 | ;; If we changed page in the mean | 449 | ;; If we changed page in the mean |
| @@ -453,7 +452,7 @@ Can be `dvi', `pdf', or `ps'.") | |||
| 453 | ;; Make sure we don't infloop. | 452 | ;; Make sure we don't infloop. |
| 454 | (file-readable-p file) | 453 | (file-readable-p file) |
| 455 | (with-selected-window win | 454 | (with-selected-window win |
| 456 | (doc-view-goto-page page)))))))) | 455 | (doc-view-goto-page page)))))))) |
| 457 | (overlay-put (doc-view-current-overlay) | 456 | (overlay-put (doc-view-current-overlay) |
| 458 | 'help-echo (doc-view-current-info)))) | 457 | 'help-echo (doc-view-current-info)))) |
| 459 | 458 | ||
| @@ -713,8 +712,8 @@ Should be invoked when the cached images aren't up-to-date." | |||
| 713 | (if (and doc-view-dvipdf-program | 712 | (if (and doc-view-dvipdf-program |
| 714 | (executable-find doc-view-dvipdf-program)) | 713 | (executable-find doc-view-dvipdf-program)) |
| 715 | (doc-view-start-process "dvi->pdf" doc-view-dvipdf-program | 714 | (doc-view-start-process "dvi->pdf" doc-view-dvipdf-program |
| 716 | (list dvi pdf) | 715 | (list dvi pdf) |
| 717 | callback) | 716 | callback) |
| 718 | (doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program | 717 | (doc-view-start-process "dvi->pdf" doc-view-dvipdfm-program |
| 719 | (list "-o" pdf dvi) | 718 | (list "-o" pdf dvi) |
| 720 | callback))) | 719 | callback))) |
| @@ -735,7 +734,7 @@ is named like ODF with the extension turned to pdf." | |||
| 735 | (list (format "-r%d" (round doc-view-resolution)) | 734 | (list (format "-r%d" (round doc-view-resolution)) |
| 736 | (concat "-sOutputFile=" png) | 735 | (concat "-sOutputFile=" png) |
| 737 | pdf-ps)) | 736 | pdf-ps)) |
| 738 | (lexical-let ((resolution doc-view-resolution)) | 737 | (let ((resolution doc-view-resolution)) |
| 739 | (lambda () | 738 | (lambda () |
| 740 | ;; Only create the resolution file when it's all done, so it also | 739 | ;; Only create the resolution file when it's all done, so it also |
| 741 | ;; serves as a witness that the conversion is complete. | 740 | ;; serves as a witness that the conversion is complete. |
| @@ -780,7 +779,7 @@ Start by converting PAGES, and then the rest." | |||
| 780 | ;; (almost) consecutive, but since in 99% of the cases, there'll be only | 779 | ;; (almost) consecutive, but since in 99% of the cases, there'll be only |
| 781 | ;; a single page anyway, and of the remaining 1%, few cases will have | 780 | ;; a single page anyway, and of the remaining 1%, few cases will have |
| 782 | ;; consecutive pages, it's not worth the trouble. | 781 | ;; consecutive pages, it's not worth the trouble. |
| 783 | (lexical-let ((pdf pdf) (png png) (rest (cdr pages))) | 782 | (let ((rest (cdr pages))) |
| 784 | (doc-view-pdf->png-1 | 783 | (doc-view-pdf->png-1 |
| 785 | pdf (format png (car pages)) (car pages) | 784 | pdf (format png (car pages)) (car pages) |
| 786 | (lambda () | 785 | (lambda () |
| @@ -793,8 +792,8 @@ Start by converting PAGES, and then the rest." | |||
| 793 | ;; not sufficient. | 792 | ;; not sufficient. |
| 794 | (dolist (win (get-buffer-window-list (current-buffer) nil 'visible)) | 793 | (dolist (win (get-buffer-window-list (current-buffer) nil 'visible)) |
| 795 | (with-selected-window win | 794 | (with-selected-window win |
| 796 | (when (stringp (get-char-property (point-min) 'display)) | 795 | (when (stringp (get-char-property (point-min) 'display)) |
| 797 | (doc-view-goto-page (doc-view-current-page))))) | 796 | (doc-view-goto-page (doc-view-current-page))))) |
| 798 | ;; Convert the rest of the pages. | 797 | ;; Convert the rest of the pages. |
| 799 | (doc-view-pdf/ps->png pdf png))))))) | 798 | (doc-view-pdf/ps->png pdf png))))))) |
| 800 | 799 | ||
| @@ -816,10 +815,8 @@ Start by converting PAGES, and then the rest." | |||
| 816 | (ps | 815 | (ps |
| 817 | ;; Doc is a PS, so convert it to PDF (which will be converted to | 816 | ;; Doc is a PS, so convert it to PDF (which will be converted to |
| 818 | ;; TXT thereafter). | 817 | ;; TXT thereafter). |
| 819 | (lexical-let ((pdf (expand-file-name "doc.pdf" | 818 | (let ((pdf (expand-file-name "doc.pdf" |
| 820 | (doc-view-current-cache-dir))) | 819 | (doc-view-current-cache-dir)))) |
| 821 | (txt txt) | ||
| 822 | (callback callback)) | ||
| 823 | (doc-view-ps->pdf doc-view-buffer-file-name pdf | 820 | (doc-view-ps->pdf doc-view-buffer-file-name pdf |
| 824 | (lambda () (doc-view-pdf->txt pdf txt callback))))) | 821 | (lambda () (doc-view-pdf->txt pdf txt callback))))) |
| 825 | (dvi | 822 | (dvi |
| @@ -873,9 +870,7 @@ Those files are saved in the directory given by the function | |||
| 873 | (dvi | 870 | (dvi |
| 874 | ;; DVI files have to be converted to PDF before Ghostscript can process | 871 | ;; DVI files have to be converted to PDF before Ghostscript can process |
| 875 | ;; it. | 872 | ;; it. |
| 876 | (lexical-let | 873 | (let ((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir))) |
| 877 | ((pdf (expand-file-name "doc.pdf" doc-view-current-cache-dir)) | ||
| 878 | (png-file png-file)) | ||
| 879 | (doc-view-dvi->pdf doc-view-buffer-file-name pdf | 874 | (doc-view-dvi->pdf doc-view-buffer-file-name pdf |
| 880 | (lambda () (doc-view-pdf/ps->png pdf png-file))))) | 875 | (lambda () (doc-view-pdf/ps->png pdf png-file))))) |
| 881 | (odf | 876 | (odf |
| @@ -1026,8 +1021,8 @@ have the page we want to view." | |||
| 1026 | (and (not (member pagefile prev-pages)) | 1021 | (and (not (member pagefile prev-pages)) |
| 1027 | (member pagefile doc-view-current-files))) | 1022 | (member pagefile doc-view-current-files))) |
| 1028 | (with-selected-window win | 1023 | (with-selected-window win |
| 1029 | (assert (eq (current-buffer) buffer)) | 1024 | (assert (eq (current-buffer) buffer)) |
| 1030 | (doc-view-goto-page page)))))))) | 1025 | (doc-view-goto-page page)))))))) |
| 1031 | 1026 | ||
| 1032 | (defun doc-view-buffer-message () | 1027 | (defun doc-view-buffer-message () |
| 1033 | ;; Only show this message initially, not when refreshing the buffer (in which | 1028 | ;; Only show this message initially, not when refreshing the buffer (in which |
| @@ -1470,9 +1465,9 @@ See the command `doc-view-mode' for more information on this mode." | |||
| 1470 | (when (not (eq major-mode 'doc-view-mode)) | 1465 | (when (not (eq major-mode 'doc-view-mode)) |
| 1471 | (doc-view-toggle-display)) | 1466 | (doc-view-toggle-display)) |
| 1472 | (with-selected-window | 1467 | (with-selected-window |
| 1473 | (or (get-buffer-window (current-buffer) 0) | 1468 | (or (get-buffer-window (current-buffer) 0) |
| 1474 | (selected-window)) | 1469 | (selected-window)) |
| 1475 | (doc-view-goto-page page))))) | 1470 | (doc-view-goto-page page))))) |
| 1476 | 1471 | ||
| 1477 | 1472 | ||
| 1478 | (provide 'doc-view) | 1473 | (provide 'doc-view) |
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index be3e1ed617c..b258524b45f 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el | |||
| @@ -119,6 +119,7 @@ | |||
| 119 | 119 | ||
| 120 | (require 'backquote) | 120 | (require 'backquote) |
| 121 | (require 'macroexp) | 121 | (require 'macroexp) |
| 122 | (require 'cconv) | ||
| 122 | (eval-when-compile (require 'cl)) | 123 | (eval-when-compile (require 'cl)) |
| 123 | 124 | ||
| 124 | (or (fboundp 'defsubst) | 125 | (or (fboundp 'defsubst) |
| @@ -2238,6 +2239,8 @@ list that represents a doc string reference. | |||
| 2238 | (let ((byte-compile-current-form nil) ; close over this for warnings. | 2239 | (let ((byte-compile-current-form nil) ; close over this for warnings. |
| 2239 | bytecomp-handler) | 2240 | bytecomp-handler) |
| 2240 | (setq form (macroexpand-all form byte-compile-macro-environment)) | 2241 | (setq form (macroexpand-all form byte-compile-macro-environment)) |
| 2242 | (if lexical-binding | ||
| 2243 | (setq form (cconv-closure-convert-toplevel form))) | ||
| 2241 | (cond ((not (consp form)) | 2244 | (cond ((not (consp form)) |
| 2242 | (byte-compile-keep-pending form)) | 2245 | (byte-compile-keep-pending form)) |
| 2243 | ((and (symbolp (car form)) | 2246 | ((and (symbolp (car form)) |
| @@ -2585,9 +2588,11 @@ If FORM is a lambda or a macro, byte-compile it as a function." | |||
| 2585 | (setq fun (cdr fun))) | 2588 | (setq fun (cdr fun))) |
| 2586 | (cond ((eq (car-safe fun) 'lambda) | 2589 | (cond ((eq (car-safe fun) 'lambda) |
| 2587 | ;; expand macros | 2590 | ;; expand macros |
| 2588 | (setq fun | 2591 | (setq fun |
| 2589 | (macroexpand-all fun | 2592 | (macroexpand-all fun |
| 2590 | byte-compile-initial-macro-environment)) | 2593 | byte-compile-initial-macro-environment)) |
| 2594 | (if lexical-binding | ||
| 2595 | (setq fun (cconv-closure-convert-toplevel fun))) | ||
| 2591 | ;; get rid of the `function' quote added by the `lambda' macro | 2596 | ;; get rid of the `function' quote added by the `lambda' macro |
| 2592 | (setq fun (cadr fun)) | 2597 | (setq fun (cadr fun)) |
| 2593 | (setq fun (if macro | 2598 | (setq fun (if macro |
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el new file mode 100644 index 00000000000..ddcc7882d82 --- /dev/null +++ b/lisp/emacs-lisp/cconv.el | |||
| @@ -0,0 +1,891 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 2 | ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. | ||
| 3 | |||
| 4 | ;; licence stuff will be added later(I don't know yet what to write here) | ||
| 5 | |||
| 6 | ;;; Commentary: | ||
| 7 | |||
| 8 | ;; This takes a piece of Elisp code, and eliminates all free variables from | ||
| 9 | ;; lambda expressions. The user entry points are cconv-closure-convert and | ||
| 10 | ;; cconv-closure-convert-toplevel(for toplevel forms). | ||
| 11 | ;; All macros should be expanded. | ||
| 12 | ;; | ||
| 13 | ;; Here is a brief explanation how this code works. | ||
| 14 | ;; Firstly, we analyse the tree by calling cconv-analyse-form. | ||
| 15 | ;; This function finds all mutated variables, all functions that are suitable | ||
| 16 | ;; for lambda lifting and all variables captured by closure. It passes the tree | ||
| 17 | ;; once, returning a list of three lists. | ||
| 18 | ;; | ||
| 19 | ;; Then we calculate the intersection of first and third lists returned by | ||
| 20 | ;; cconv-analyse form to find all mutated variables that are captured by | ||
| 21 | ;; closure. | ||
| 22 | |||
| 23 | ;; Armed with this data, we call cconv-closure-convert-rec, that rewrites the | ||
| 24 | ;; tree recursivly, lifting lambdas where possible, building closures where it | ||
| 25 | ;; is needed and eliminating mutable variables used in closure. | ||
| 26 | ;; | ||
| 27 | ;; We do following replacements : | ||
| 28 | ;; (lambda (v1 ...) ... fv1 fv2 ...) => (lambda (v1 ... fv1 fv2 ) ... fv1 fv2 .) | ||
| 29 | ;; if the function is suitable for lambda lifting (if all calls are known) | ||
| 30 | ;; | ||
| 31 | ;; (function (lambda (v1 ...) ... fv ...)) => | ||
| 32 | ;; (curry (lambda (env v1 ...) ... env ...) env) | ||
| 33 | ;; if the function has only 1 free variable | ||
| 34 | ;; | ||
| 35 | ;; and finally | ||
| 36 | ;; (function (lambda (v1 ...) ... fv1 fv2 ...)) => | ||
| 37 | ;; (curry (lambda (env v1 ..) .. (aref env 0) (aref env 1) ..) (vector fv1 fv2)) | ||
| 38 | ;; if the function has 2 or more free variables | ||
| 39 | ;; | ||
| 40 | ;; If the function has no free variables, we don't do anything. | ||
| 41 | ;; | ||
| 42 | ;; If the variable is mutable(updated by setq), and it is used in closure | ||
| 43 | ;; we wrap it's definition with list: (list var) and we also replace | ||
| 44 | ;; var => (car var) wherever this variable is used, and also | ||
| 45 | ;; (setq var value) => (setcar var value) where it is updated. | ||
| 46 | ;; | ||
| 47 | ;; If defun argument is closure mutable, we letbind it and wrap it's | ||
| 48 | ;; definition with list. | ||
| 49 | ;; (defun foo (... mutable-arg ...) ...) => | ||
| 50 | ;; (defun foo (... m-arg ...) (let ((m-arg (list m-arg))) ...)) | ||
| 51 | ;; | ||
| 52 | ;; | ||
| 53 | ;; | ||
| 54 | ;; | ||
| 55 | ;; | ||
| 56 | ;;; Code: | ||
| 57 | |||
| 58 | (require 'pcase) | ||
| 59 | (eval-when-compile (require 'cl)) | ||
| 60 | |||
| 61 | (defconst cconv-liftwhen 3 | ||
| 62 | "Try to do lambda lifting if the number of arguments + free variables | ||
| 63 | is less than this number.") | ||
| 64 | (defvar cconv-mutated | ||
| 65 | "List of mutated variables in current form") | ||
| 66 | (defvar cconv-captured | ||
| 67 | "List of closure captured variables in current form") | ||
| 68 | (defvar cconv-captured+mutated | ||
| 69 | "An intersection between cconv-mutated and cconv-captured lists.") | ||
| 70 | (defvar cconv-lambda-candidates | ||
| 71 | "List of candidates for lambda lifting") | ||
| 72 | |||
| 73 | |||
| 74 | |||
| 75 | (defun cconv-freevars (form &optional fvrs) | ||
| 76 | "Find all free variables of given form. | ||
| 77 | Arguments: | ||
| 78 | -- FORM is a piece of Elisp code after macroexpansion. | ||
| 79 | -- FVRS(optional) is a list of variables already found. Used for recursive tree | ||
| 80 | traversal | ||
| 81 | |||
| 82 | Returns a list of free variables." | ||
| 83 | ;; If a leaf in the tree is a symbol, but it is not a global variable, not a | ||
| 84 | ;; keyword, not 'nil or 't we consider this leaf as a variable. | ||
| 85 | ;; Free variables are the variables that are not declared above in this tree. | ||
| 86 | ;; For example free variables of (lambda (a1 a2 ..) body-forms) are | ||
| 87 | ;; free variables of body-forms excluding a1, a2 .. | ||
| 88 | ;; Free variables of (let ((v1 ..) (v2) ..)) body-forms) are | ||
| 89 | ;; free variables of body-forms excluding v1, v2 ... | ||
| 90 | ;; and so on. | ||
| 91 | |||
| 92 | ;; a list of free variables already found(FVRS) is passed in parameter | ||
| 93 | ;; to try to use cons or push where possible, and to minimize the usage | ||
| 94 | ;; of append | ||
| 95 | |||
| 96 | ;; This function can contain duplicates(because we use 'append instead | ||
| 97 | ;; of union of two sets - for performance reasons). | ||
| 98 | (pcase form | ||
| 99 | (`(let ,varsvalues . ,body-forms) ; let special form | ||
| 100 | (let ((fvrs-1 '())) | ||
| 101 | (dolist (exp body-forms) | ||
| 102 | (setq fvrs-1 (cconv-freevars exp fvrs-1))) | ||
| 103 | (dolist (elm varsvalues) | ||
| 104 | (if (listp elm) | ||
| 105 | (setq fvrs-1 (delq (car elm) fvrs-1)) | ||
| 106 | (setq fvrs-1 (delq elm fvrs-1)))) | ||
| 107 | (setq fvrs (append fvrs fvrs-1)) | ||
| 108 | (dolist (exp varsvalues) | ||
| 109 | (when (listp exp) (setq fvrs (cconv-freevars (cadr exp) fvrs)))) | ||
| 110 | fvrs)) | ||
| 111 | |||
| 112 | (`(let* ,varsvalues . ,body-forms) ; let* special form | ||
| 113 | (let ((vrs '()) | ||
| 114 | (fvrs-1 '())) | ||
| 115 | (dolist (exp varsvalues) | ||
| 116 | (if (listp exp) | ||
| 117 | (progn | ||
| 118 | (setq fvrs-1 (cconv-freevars (cadr exp) fvrs-1)) | ||
| 119 | (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) | ||
| 120 | (push (car exp) vrs)) | ||
| 121 | (progn | ||
| 122 | (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) | ||
| 123 | (push exp vrs)))) | ||
| 124 | (dolist (exp body-forms) | ||
| 125 | (setq fvrs-1 (cconv-freevars exp fvrs-1))) | ||
| 126 | (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) | ||
| 127 | (append fvrs fvrs-1))) | ||
| 128 | |||
| 129 | (`((lambda . ,_) . ,_) ; first element is lambda expression | ||
| 130 | (dolist (exp `((function ,(car form)) . ,(cdr form))) | ||
| 131 | (setq fvrs (cconv-freevars exp fvrs))) fvrs) | ||
| 132 | |||
| 133 | (`(cond . ,cond-forms) ; cond special form | ||
| 134 | (dolist (exp1 cond-forms) | ||
| 135 | (dolist (exp2 exp1) | ||
| 136 | (setq fvrs (cconv-freevars exp2 fvrs)))) fvrs) | ||
| 137 | |||
| 138 | (`(quote . ,_) fvrs) ; quote form | ||
| 139 | |||
| 140 | (`(function . ((lambda ,vars . ,body-forms))) | ||
| 141 | (let ((functionform (cadr form)) (fvrs-1 '())) | ||
| 142 | (dolist (exp body-forms) | ||
| 143 | (setq fvrs-1 (cconv-freevars exp fvrs-1))) | ||
| 144 | (dolist (elm vars) (setq fvrs-1 (delq elm fvrs-1))) | ||
| 145 | (append fvrs fvrs-1))) ; function form | ||
| 146 | |||
| 147 | (`(function . ,_) fvrs) ; same as quote | ||
| 148 | ;condition-case | ||
| 149 | (`(condition-case ,var ,protected-form . ,conditions-bodies) | ||
| 150 | (let ((fvrs-1 '())) | ||
| 151 | (setq fvrs-1 (cconv-freevars protected-form '())) | ||
| 152 | (dolist (exp conditions-bodies) | ||
| 153 | (setq fvrs-1 (cconv-freevars (cadr exp) fvrs-1))) | ||
| 154 | (setq fvrs-1 (delq var fvrs-1)) | ||
| 155 | (append fvrs fvrs-1))) | ||
| 156 | |||
| 157 | (`(,(and sym (or `defun `defconst `defvar)) . ,_) | ||
| 158 | ;; we call cconv-freevars only for functions(lambdas) | ||
| 159 | ;; defun, defconst, defvar are not allowed to be inside | ||
| 160 | ;; a function(lambda) | ||
| 161 | (error "Invalid form: %s inside a function" sym)) | ||
| 162 | |||
| 163 | (`(,_ . ,body-forms) ; first element is a function or whatever | ||
| 164 | (dolist (exp body-forms) | ||
| 165 | (setq fvrs (cconv-freevars exp fvrs))) fvrs) | ||
| 166 | |||
| 167 | (_ (if (or (not (symbolp form)) ; form is not a list | ||
| 168 | (special-variable-p form) | ||
| 169 | (memq form '(nil t)) | ||
| 170 | (keywordp form)) | ||
| 171 | fvrs | ||
| 172 | (cons form fvrs))))) | ||
| 173 | |||
| 174 | ;;;###autoload | ||
| 175 | (defun cconv-closure-convert (form &optional toplevel) | ||
| 176 | ;; cconv-closure-convert-rec has a lot of parameters that are | ||
| 177 | ;; whether useless for user, whether they should contain | ||
| 178 | ;; specific data like a list of closure mutables or the list | ||
| 179 | ;; of lambdas suitable for lifting. | ||
| 180 | ;; | ||
| 181 | ;; That's why this function exists. | ||
| 182 | "Main entry point for non-toplevel forms. | ||
| 183 | -- FORM is a piece of Elisp code after macroexpansion. | ||
| 184 | -- TOPLEVEL(optional) is a boolean variable, true if we are at the root of AST | ||
| 185 | |||
| 186 | Returns a form where all lambdas don't have any free variables." | ||
| 187 | (let ((cconv-mutated '()) | ||
| 188 | (cconv-lambda-candidates '()) | ||
| 189 | (cconv-captured '()) | ||
| 190 | (cconv-captured+mutated '())) | ||
| 191 | ;; Analyse form - fill these variables with new information | ||
| 192 | (cconv-analyse-form form '() nil) | ||
| 193 | ;; Calculate an intersection of cconv-mutated and cconv-captured | ||
| 194 | (dolist (mvr cconv-mutated) | ||
| 195 | (when (memq mvr cconv-captured) ; | ||
| 196 | (push mvr cconv-captured+mutated))) | ||
| 197 | (cconv-closure-convert-rec | ||
| 198 | form ; the tree | ||
| 199 | '() ; | ||
| 200 | '() ; fvrs initially empty | ||
| 201 | '() ; envs initially empty | ||
| 202 | '() | ||
| 203 | toplevel))) ; true if the tree is a toplevel form | ||
| 204 | |||
| 205 | ;;;###autoload | ||
| 206 | (defun cconv-closure-convert-toplevel (form) | ||
| 207 | "Entry point for toplevel forms. | ||
| 208 | -- FORM is a piece of Elisp code after macroexpansion. | ||
| 209 | |||
| 210 | Returns a form where all lambdas don't have any free variables." | ||
| 211 | ;; we distinguish toplevel forms to treat def(un|var|const) correctly. | ||
| 212 | (cconv-closure-convert form t)) | ||
| 213 | |||
| 214 | (defun cconv-closure-convert-rec | ||
| 215 | (form emvrs fvrs envs lmenvs defs-are-legal) | ||
| 216 | ;; This function actually rewrites the tree. | ||
| 217 | "Eliminates all free variables of all lambdas in given forms. | ||
| 218 | Arguments: | ||
| 219 | -- FORM is a piece of Elisp code after macroexpansion. | ||
| 220 | -- LMENVS is a list of environments used for lambda-lifting. Initially empty. | ||
| 221 | -- EMVRS is a list that contains mutated variables that are visible | ||
| 222 | within current environment. | ||
| 223 | -- ENVS is an environment(list of free variables) of current closure. | ||
| 224 | Initially empty. | ||
| 225 | -- FVRS is a list of variables to substitute in each context. | ||
| 226 | Initially empty. | ||
| 227 | -- DEFS-ARE-LEGAL is a boolean variable, true if def(un|var|const) | ||
| 228 | can be used in this form(e.g. toplevel form) | ||
| 229 | |||
| 230 | Returns a form where all lambdas don't have any free variables." | ||
| 231 | ;; What's the difference between fvrs and envs? | ||
| 232 | ;; Suppose that we have the code | ||
| 233 | ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1))) | ||
| 234 | ;; only the first occurrence of fvr should be replaced by | ||
| 235 | ;; (aref env ...). | ||
| 236 | ;; So initially envs and fvrs are the same thing, but when we descend to | ||
| 237 | ;; the 'let, we delete fvr from fvrs. Why we don't delete fvr from envs? | ||
| 238 | ;; Because in envs the order of variables is important. We use this list | ||
| 239 | ;; to find the number of a specific variable in the environment vector, | ||
| 240 | ;; so we never touch it(unless we enter to the other closure). | ||
| 241 | ;;(if (listp form) (print (car form)) form) | ||
| 242 | (pcase form | ||
| 243 | (`(,(and letsym (or `let* `let)) ,varsvalues . ,body-forms) | ||
| 244 | |||
| 245 | ; let and let* special forms | ||
| 246 | (let ((body-forms-new '()) | ||
| 247 | (varsvalues-new '()) | ||
| 248 | ;; next for variables needed for delayed push | ||
| 249 | ;; because we should process <value(s)> | ||
| 250 | ;; before we change any arguments | ||
| 251 | (lmenvs-new '()) ;needed only in case of let | ||
| 252 | (emvrs-new '()) ;needed only in case of let | ||
| 253 | (emvr-push) ;needed only in case of let* | ||
| 254 | (lmenv-push)) ;needed only in case of let* | ||
| 255 | |||
| 256 | (dolist (elm varsvalues) ;begin of dolist over varsvalues | ||
| 257 | (let (var value elm-new iscandidate ismutated) | ||
| 258 | (if (listp elm) ; (let (v1) ...) => (let ((v1 nil)) ...) | ||
| 259 | (progn | ||
| 260 | (setq var (car elm)) | ||
| 261 | (setq value (cadr elm))) | ||
| 262 | (setq var elm)) | ||
| 263 | |||
| 264 | ;; Check if var is a candidate for lambda lifting | ||
| 265 | (let ((lcandid cconv-lambda-candidates)) | ||
| 266 | (while (and lcandid (not iscandidate)) | ||
| 267 | (when (and (eq (caar lcandid) var) | ||
| 268 | (eq (caddar lcandid) elm) | ||
| 269 | (eq (cadr (cddar lcandid)) form)) | ||
| 270 | (setq iscandidate t)) | ||
| 271 | (setq lcandid (cdr lcandid)))) | ||
| 272 | |||
| 273 | ; declared variable is a candidate | ||
| 274 | ; for lambda lifting | ||
| 275 | (if iscandidate | ||
| 276 | (let* ((func (cadr elm)) ; function(lambda) itself | ||
| 277 | ; free variables | ||
| 278 | (fv (delete-dups (cconv-freevars func '()))) | ||
| 279 | (funcvars (append fv (cadadr func))) ;function args | ||
| 280 | (funcbodies (cddadr func)) ; function bodies | ||
| 281 | (funcbodies-new '())) | ||
| 282 | ; lambda lifting condition | ||
| 283 | (if (or (not fv) (< cconv-liftwhen (length funcvars))) | ||
| 284 | ; do not lift | ||
| 285 | (setq | ||
| 286 | elm-new | ||
| 287 | `(,var | ||
| 288 | ,(cconv-closure-convert-rec | ||
| 289 | func emvrs fvrs envs lmenvs nil))) | ||
| 290 | ; lift | ||
| 291 | (progn | ||
| 292 | (dolist (elm2 funcbodies) | ||
| 293 | (push ; convert function bodies | ||
| 294 | (cconv-closure-convert-rec | ||
| 295 | elm2 emvrs nil envs lmenvs nil) | ||
| 296 | funcbodies-new)) | ||
| 297 | (if (eq letsym 'let*) | ||
| 298 | (setq lmenv-push (cons var fv)) | ||
| 299 | (push (cons var fv) lmenvs-new)) | ||
| 300 | ; push lifted function | ||
| 301 | |||
| 302 | (setq elm-new | ||
| 303 | `(,var | ||
| 304 | (function . | ||
| 305 | ((lambda ,funcvars . | ||
| 306 | ,(reverse funcbodies-new))))))))) | ||
| 307 | |||
| 308 | ;declared variable is not a function | ||
| 309 | (progn | ||
| 310 | ;; Check if var is mutated | ||
| 311 | (let ((lmutated cconv-captured+mutated)) | ||
| 312 | (while (and lmutated (not ismutated)) | ||
| 313 | (when (and (eq (caar lmutated) var) | ||
| 314 | (eq (caddar lmutated) elm) | ||
| 315 | (eq (cadr (cddar lmutated)) form)) | ||
| 316 | (setq ismutated t)) | ||
| 317 | (setq lmutated (cdr lmutated)))) | ||
| 318 | (if ismutated | ||
| 319 | (progn ; declared variable is mutated | ||
| 320 | (setq elm-new | ||
| 321 | `(,var (list ,(cconv-closure-convert-rec | ||
| 322 | value emvrs | ||
| 323 | fvrs envs lmenvs nil)))) | ||
| 324 | (if (eq letsym 'let*) | ||
| 325 | (setq emvr-push var) | ||
| 326 | (push var emvrs-new))) | ||
| 327 | (progn | ||
| 328 | (setq | ||
| 329 | elm-new | ||
| 330 | `(,var ; else | ||
| 331 | ,(cconv-closure-convert-rec | ||
| 332 | value emvrs fvrs envs lmenvs nil))))))) | ||
| 333 | |||
| 334 | ;; this piece of code below letbinds free | ||
| 335 | ;; variables of a lambda lifted function | ||
| 336 | ;; if they are redefined in this let | ||
| 337 | ;; example: | ||
| 338 | ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1)) | ||
| 339 | ;; Here we can not pass y as parameter because it is | ||
| 340 | ;; redefined. We add a (closed-y y) declaration. | ||
| 341 | ;; We do that even if the function is not used inside | ||
| 342 | ;; this let(*). The reason why we ignore this case is | ||
| 343 | ;; that we can't "look forward" to see if the function | ||
| 344 | ;; is called there or not. To treat well this case we | ||
| 345 | ;; need to traverse the tree one more time to collect this | ||
| 346 | ;; data, and I think that it's not worth it. | ||
| 347 | |||
| 348 | (when (eq letsym 'let*) | ||
| 349 | (let ((closedsym '()) | ||
| 350 | (new-lmenv '()) | ||
| 351 | (old-lmenv '())) | ||
| 352 | (dolist (lmenv lmenvs) | ||
| 353 | (when (memq var (cdr lmenv)) | ||
| 354 | (setq closedsym | ||
| 355 | (make-symbol | ||
| 356 | (concat "closed-" (symbol-name var)))) | ||
| 357 | (setq new-lmenv (list (car lmenv))) | ||
| 358 | (dolist (frv (cdr lmenv)) (if (eq frv var) | ||
| 359 | (push closedsym new-lmenv) | ||
| 360 | (push frv new-lmenv))) | ||
| 361 | (setq new-lmenv (reverse new-lmenv)) | ||
| 362 | (setq old-lmenv lmenv))) | ||
| 363 | (when new-lmenv | ||
| 364 | (setq lmenvs (remq old-lmenv lmenvs)) | ||
| 365 | (push new-lmenv lmenvs) | ||
| 366 | (push `(,closedsym ,var) varsvalues-new)))) | ||
| 367 | ;; we push the element after redefined free variables | ||
| 368 | ;; are processes. this is important to avoid the bug | ||
| 369 | ;; when free variable and the function have the same | ||
| 370 | ;; name | ||
| 371 | (push elm-new varsvalues-new) | ||
| 372 | |||
| 373 | (when (eq letsym 'let*) ; update fvrs | ||
| 374 | (setq fvrs (remq var fvrs)) | ||
| 375 | (setq emvrs (remq var emvrs)) ; remove if redefined | ||
| 376 | (when emvr-push | ||
| 377 | (push emvr-push emvrs) | ||
| 378 | (setq emvr-push nil)) | ||
| 379 | (let (lmenvs-1) ; remove var from lmenvs if redefined | ||
| 380 | (dolist (iter lmenvs) | ||
| 381 | (when (not (assq var lmenvs)) | ||
| 382 | (push iter lmenvs-1))) | ||
| 383 | (setq lmenvs lmenvs-1)) | ||
| 384 | (when lmenv-push | ||
| 385 | (push lmenv-push lmenvs) | ||
| 386 | (setq lmenv-push nil))) | ||
| 387 | )) ; end of dolist over varsvalues | ||
| 388 | (when (eq letsym 'let) | ||
| 389 | |||
| 390 | (let (var fvrs-1 emvrs-1 lmenvs-1) | ||
| 391 | ;; Here we update emvrs, fvrs and lmenvs lists | ||
| 392 | (dolist (vr fvrs) | ||
| 393 | ; safely remove | ||
| 394 | (when (not (assq vr varsvalues-new)) (push vr fvrs-1))) | ||
| 395 | (setq fvrs fvrs-1) | ||
| 396 | (dolist (vr emvrs) | ||
| 397 | ; safely remove | ||
| 398 | (when (not (assq vr varsvalues-new)) (push vr emvrs-1))) | ||
| 399 | (setq emvrs emvrs-1) | ||
| 400 | ; push new | ||
| 401 | (setq emvrs (append emvrs emvrs-new)) | ||
| 402 | (dolist (vr lmenvs) | ||
| 403 | (when (not (assq (car vr) varsvalues-new)) | ||
| 404 | (push vr lmenvs-1))) | ||
| 405 | (setq lmenvs (append lmenvs lmenvs-new))) | ||
| 406 | |||
| 407 | ;; Here we do the same letbinding as for let* above | ||
| 408 | ;; to avoid situation when a free variable of a lambda lifted | ||
| 409 | ;; function got redefined. | ||
| 410 | |||
| 411 | (let ((new-lmenv) | ||
| 412 | (var nil) | ||
| 413 | (closedsym nil) | ||
| 414 | (letbinds '()) | ||
| 415 | (fvrs-new)) ; list of (closed-var var) | ||
| 416 | (dolist (elm varsvalues) | ||
| 417 | (if (listp elm) | ||
| 418 | (setq var (car elm)) | ||
| 419 | (setq var elm)) | ||
| 420 | |||
| 421 | (let ((lmenvs-1 lmenvs)) ; just to avoid manipulating | ||
| 422 | (dolist (lmenv lmenvs-1) ; the counter inside the loop | ||
| 423 | (when (memq var (cdr lmenv)) | ||
| 424 | (setq closedsym (make-symbol | ||
| 425 | (concat "closed-" | ||
| 426 | (symbol-name var)))) | ||
| 427 | |||
| 428 | (setq new-lmenv (list (car lmenv))) | ||
| 429 | (dolist (frv (cdr lmenv)) (if (eq frv var) | ||
| 430 | (push closedsym new-lmenv) | ||
| 431 | (push frv new-lmenv))) | ||
| 432 | (setq new-lmenv (reverse new-lmenv)) | ||
| 433 | (setq lmenvs (remq lmenv lmenvs)) | ||
| 434 | (push new-lmenv lmenvs) | ||
| 435 | (push `(,closedsym ,var) letbinds) | ||
| 436 | )))) | ||
| 437 | (setq varsvalues-new (append varsvalues-new letbinds)))) | ||
| 438 | |||
| 439 | (dolist (elm body-forms) ; convert body forms | ||
| 440 | (push (cconv-closure-convert-rec | ||
| 441 | elm emvrs fvrs envs lmenvs nil) | ||
| 442 | body-forms-new)) | ||
| 443 | `(,letsym ,(reverse varsvalues-new) . ,(reverse body-forms-new)))) | ||
| 444 | ;end of let let* forms | ||
| 445 | |||
| 446 | ; first element is lambda expression | ||
| 447 | (`(,(and `(lambda . ,_) fun) . ,other-body-forms) | ||
| 448 | |||
| 449 | (let ((other-body-forms-new '())) | ||
| 450 | (dolist (elm other-body-forms) | ||
| 451 | (push (cconv-closure-convert-rec | ||
| 452 | elm emvrs fvrs envs lmenvs nil) | ||
| 453 | other-body-forms-new)) | ||
| 454 | (cons | ||
| 455 | (cadr | ||
| 456 | (cconv-closure-convert-rec | ||
| 457 | (list 'function fun) emvrs fvrs envs lmenvs nil)) | ||
| 458 | (reverse other-body-forms-new)))) | ||
| 459 | |||
| 460 | (`(cond . ,cond-forms) ; cond special form | ||
| 461 | (let ((cond-forms-new '())) | ||
| 462 | (dolist (elm cond-forms) | ||
| 463 | (push (let ((elm-new '())) | ||
| 464 | (dolist (elm-2 elm) | ||
| 465 | (push | ||
| 466 | (cconv-closure-convert-rec | ||
| 467 | elm-2 emvrs fvrs envs lmenvs nil) | ||
| 468 | elm-new)) | ||
| 469 | (reverse elm-new)) | ||
| 470 | cond-forms-new)) | ||
| 471 | (cons 'cond | ||
| 472 | (reverse cond-forms-new)))) | ||
| 473 | |||
| 474 | (`(quote . ,_) form) ; quote form | ||
| 475 | |||
| 476 | (`(function . ((lambda ,vars . ,body-forms))) ; function form | ||
| 477 | (let (fvrs-new) ; we remove vars from fvrs | ||
| 478 | (dolist (elm fvrs) ;i use such a tricky way to avoid side effects | ||
| 479 | (when (not (memq elm vars)) | ||
| 480 | (push elm fvrs-new))) | ||
| 481 | (setq fvrs fvrs-new)) | ||
| 482 | (let* ((fv (delete-dups (cconv-freevars form '()))) | ||
| 483 | (leave fvrs) ; leave = non nil if we should leave env unchanged | ||
| 484 | (body-forms-new '()) | ||
| 485 | (letbind '()) | ||
| 486 | (mv nil) | ||
| 487 | (envector nil)) | ||
| 488 | (when fv | ||
| 489 | ;; Here we form our environment vector. | ||
| 490 | ;; If outer closure contains all | ||
| 491 | ;; free variables of this function(and nothing else) | ||
| 492 | ;; then we use the same environment vector as for outer closure, | ||
| 493 | ;; i.e. we leave the environment vector unchanged | ||
| 494 | ;; otherwise we build a new environmet vector | ||
| 495 | (if (eq (length envs) (length fv)) | ||
| 496 | (let ((fv-temp fv)) | ||
| 497 | (while (and fv-temp leave) | ||
| 498 | (when (not (memq (car fv-temp) fvrs)) (setq leave nil)) | ||
| 499 | (setq fv-temp (cdr fv-temp)))) | ||
| 500 | (setq leave nil)) | ||
| 501 | |||
| 502 | (if (not leave) | ||
| 503 | (progn | ||
| 504 | (dolist (elm fv) | ||
| 505 | (push | ||
| 506 | (cconv-closure-convert-rec | ||
| 507 | elm (remq elm emvrs) fvrs envs lmenvs nil) | ||
| 508 | envector)) ; process vars for closure vector | ||
| 509 | (setq envector (reverse envector)) | ||
| 510 | (setq envs fv)) | ||
| 511 | (setq envector `(env))) ; leave unchanged | ||
| 512 | (setq fvrs fv)) ; update substitution list | ||
| 513 | |||
| 514 | ;; the difference between envs and fvrs is explained | ||
| 515 | ;; in comment in the beginning of the function | ||
| 516 | (dolist (elm cconv-captured+mutated) ; find mutated arguments | ||
| 517 | (setq mv (car elm)) ; used in inner closures | ||
| 518 | (when (and (memq mv vars) (eq form (caddr elm))) | ||
| 519 | (progn (push mv emvrs) | ||
| 520 | (push `(,mv (list ,mv)) letbind)))) | ||
| 521 | (dolist (elm body-forms) ; convert function body | ||
| 522 | (push (cconv-closure-convert-rec | ||
| 523 | elm emvrs fvrs envs lmenvs nil) | ||
| 524 | body-forms-new)) | ||
| 525 | |||
| 526 | (setq body-forms-new | ||
| 527 | (if letbind `((let ,letbind . ,(reverse body-forms-new))) | ||
| 528 | (reverse body-forms-new))) | ||
| 529 | |||
| 530 | (cond | ||
| 531 | ;if no freevars - do nothing | ||
| 532 | ((null envector) | ||
| 533 | `(function (lambda ,vars . ,body-forms-new))) | ||
| 534 | ; 1 free variable - do not build vector | ||
| 535 | ((null (cdr envector)) | ||
| 536 | `(curry | ||
| 537 | (function (lambda (env . ,vars) . ,body-forms-new)) | ||
| 538 | ,(car envector))) | ||
| 539 | ; >=2 free variables - build vector | ||
| 540 | (t | ||
| 541 | `(curry | ||
| 542 | (function (lambda (env . ,vars) . ,body-forms-new)) | ||
| 543 | (vector . ,envector)))))) | ||
| 544 | |||
| 545 | (`(function . ,_) form) ; same as quote | ||
| 546 | |||
| 547 | ;defconst, defvar | ||
| 548 | (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,body-forms) | ||
| 549 | |||
| 550 | (if defs-are-legal | ||
| 551 | (let ((body-forms-new '())) | ||
| 552 | (dolist (elm body-forms) | ||
| 553 | (push (cconv-closure-convert-rec | ||
| 554 | elm emvrs fvrs envs lmenvs nil) | ||
| 555 | body-forms-new)) | ||
| 556 | (setq body-forms-new (reverse body-forms-new)) | ||
| 557 | `(,sym ,definedsymbol . ,body-forms-new)) | ||
| 558 | (error "Invalid form: %s inside a function" sym))) | ||
| 559 | |||
| 560 | ;defun, defmacro, defsubst | ||
| 561 | (`(,(and sym (or `defun `defmacro `defsubst)) | ||
| 562 | ,func ,vars . ,body-forms) | ||
| 563 | (if defs-are-legal | ||
| 564 | (let ((body-new '()) ; the whole body | ||
| 565 | (body-forms-new '()) ; body w\o docstring and interactive | ||
| 566 | (letbind '())) | ||
| 567 | ; find mutable arguments | ||
| 568 | (let ((lmutated cconv-captured+mutated) ismutated) | ||
| 569 | (dolist (elm vars) | ||
| 570 | (setq ismutated nil) | ||
| 571 | (while (and lmutated (not ismutated)) | ||
| 572 | (when (and (eq (caar lmutated) elm) | ||
| 573 | (eq (cadar lmutated) form)) | ||
| 574 | (setq ismutated t)) | ||
| 575 | (setq lmutated (cdr lmutated))) | ||
| 576 | (when ismutated | ||
| 577 | (push elm letbind) | ||
| 578 | (push elm emvrs)))) | ||
| 579 | ;transform body-forms | ||
| 580 | (when (stringp (car body-forms)) ; treat docstring well | ||
| 581 | (push (car body-forms) body-new) | ||
| 582 | (setq body-forms (cdr body-forms))) | ||
| 583 | (when (and (listp (car body-forms)) ; treat (interactive) well | ||
| 584 | (eq (caar body-forms) 'interactive)) | ||
| 585 | (push | ||
| 586 | (cconv-closure-convert-rec | ||
| 587 | (car body-forms) | ||
| 588 | emvrs fvrs envs lmenvs nil) body-new) | ||
| 589 | (setq body-forms (cdr body-forms))) | ||
| 590 | |||
| 591 | (dolist (elm body-forms) | ||
| 592 | (push (cconv-closure-convert-rec | ||
| 593 | elm emvrs fvrs envs lmenvs nil) | ||
| 594 | body-forms-new)) | ||
| 595 | (setq body-forms-new (reverse body-forms-new)) | ||
| 596 | |||
| 597 | (if letbind | ||
| 598 | ; letbind mutable arguments | ||
| 599 | (let ((varsvalues-new '())) | ||
| 600 | (dolist (elm letbind) (push `(,elm (list ,elm)) | ||
| 601 | varsvalues-new)) | ||
| 602 | (push `(let ,(reverse varsvalues-new) . | ||
| 603 | ,body-forms-new) body-new) | ||
| 604 | (setq body-new (reverse body-new))) | ||
| 605 | (setq body-new (append (reverse body-new) body-forms-new))) | ||
| 606 | |||
| 607 | `(,sym ,func ,vars . ,body-new)) | ||
| 608 | |||
| 609 | (error "Invalid form: defun inside a function"))) | ||
| 610 | ;condition-case | ||
| 611 | (`(condition-case ,var ,protected-form . ,conditions-bodies) | ||
| 612 | (let ((conditions-bodies-new '())) | ||
| 613 | (setq fvrs (remq var fvrs)) | ||
| 614 | (dolist (elm conditions-bodies) | ||
| 615 | (push (let ((elm-new '())) | ||
| 616 | (dolist (elm-2 (cdr elm)) | ||
| 617 | (push | ||
| 618 | (cconv-closure-convert-rec | ||
| 619 | elm-2 emvrs fvrs envs lmenvs nil) | ||
| 620 | elm-new)) | ||
| 621 | (cons (car elm) (reverse elm-new))) | ||
| 622 | conditions-bodies-new)) | ||
| 623 | `(condition-case | ||
| 624 | ,var | ||
| 625 | ,(cconv-closure-convert-rec | ||
| 626 | protected-form emvrs fvrs envs lmenvs nil) | ||
| 627 | . ,(reverse conditions-bodies-new)))) | ||
| 628 | |||
| 629 | (`(setq . ,forms) ; setq special form | ||
| 630 | (let (prognlist sym sym-new value) | ||
| 631 | (while forms | ||
| 632 | (setq sym (car forms)) | ||
| 633 | (setq sym-new (cconv-closure-convert-rec | ||
| 634 | sym | ||
| 635 | (remq sym emvrs) fvrs envs lmenvs nil)) | ||
| 636 | (setq value | ||
| 637 | (cconv-closure-convert-rec | ||
| 638 | (cadr forms) emvrs fvrs envs lmenvs nil)) | ||
| 639 | (if (memq sym emvrs) | ||
| 640 | (push `(setcar ,sym-new ,value) prognlist) | ||
| 641 | (if (symbolp sym-new) | ||
| 642 | (push `(setq ,sym-new ,value) prognlist) | ||
| 643 | (push `(set ,sym-new ,value) prognlist))) | ||
| 644 | (setq forms (cddr forms))) | ||
| 645 | (if (cdr prognlist) | ||
| 646 | `(progn . ,(reverse prognlist)) | ||
| 647 | (car prognlist)))) | ||
| 648 | |||
| 649 | (`(,(and (or `funcall `apply) callsym) ,fun . ,args) | ||
| 650 | ; funcall is not a special form | ||
| 651 | ; but we treat it separately | ||
| 652 | ; for the needs of lambda lifting | ||
| 653 | (let ((fv (cdr (assq fun lmenvs)))) | ||
| 654 | (if fv | ||
| 655 | (let ((args-new '()) | ||
| 656 | (processed-fv '())) | ||
| 657 | ;; All args (free variables and actual arguments) | ||
| 658 | ;; should be processed, because they can be fvrs | ||
| 659 | ;; (free variables of another closure) | ||
| 660 | (dolist (fvr fv) | ||
| 661 | (push (cconv-closure-convert-rec | ||
| 662 | fvr (remq fvr emvrs) | ||
| 663 | fvrs envs lmenvs nil) | ||
| 664 | processed-fv)) | ||
| 665 | (setq processed-fv (reverse processed-fv)) | ||
| 666 | (dolist (elm args) | ||
| 667 | (push (cconv-closure-convert-rec | ||
| 668 | elm emvrs fvrs envs lmenvs nil) | ||
| 669 | args-new)) | ||
| 670 | (setq args-new (append processed-fv (reverse args-new))) | ||
| 671 | (setq fun (cconv-closure-convert-rec | ||
| 672 | fun emvrs fvrs envs lmenvs nil)) | ||
| 673 | `(,callsym ,fun . ,args-new)) | ||
| 674 | (let ((cdr-new '())) | ||
| 675 | (dolist (elm (cdr form)) | ||
| 676 | (push (cconv-closure-convert-rec | ||
| 677 | elm emvrs fvrs envs lmenvs nil) | ||
| 678 | cdr-new)) | ||
| 679 | `(,callsym . ,(reverse cdr-new)))))) | ||
| 680 | |||
| 681 | (`(,func . ,body-forms) ; first element is function or whatever | ||
| 682 | ; function-like forms are: | ||
| 683 | ; or, and, if, progn, prog1, prog2, | ||
| 684 | ; while, until | ||
| 685 | (let ((body-forms-new '())) | ||
| 686 | (dolist (elm body-forms) | ||
| 687 | (push (cconv-closure-convert-rec | ||
| 688 | elm emvrs fvrs envs lmenvs defs-are-legal) | ||
| 689 | body-forms-new)) | ||
| 690 | (setq body-forms-new (reverse body-forms-new)) | ||
| 691 | `(,func . ,body-forms-new))) | ||
| 692 | |||
| 693 | (_ | ||
| 694 | (if (memq form fvrs) ;form is a free variable | ||
| 695 | (let* ((numero (position form envs)) | ||
| 696 | (var '())) | ||
| 697 | (assert numero) | ||
| 698 | (if (null (cdr envs)) | ||
| 699 | (setq var 'env) | ||
| 700 | ;replace form => | ||
| 701 | ;(aref env #) | ||
| 702 | (setq var `(aref env ,numero))) | ||
| 703 | (if (memq form emvrs) ; form => (car (aref env #)) if mutable | ||
| 704 | `(car ,var) | ||
| 705 | var)) | ||
| 706 | (if (memq form emvrs) ; if form is a mutable variable | ||
| 707 | `(car ,form) ; replace form => (car form) | ||
| 708 | form))))) | ||
| 709 | |||
| 710 | (defun cconv-analyse-form (form vars inclosure) | ||
| 711 | |||
| 712 | "Find mutated variables and variables captured by closure. Analyse | ||
| 713 | lambdas if they are suitable for lambda lifting. | ||
| 714 | -- FORM is a piece of Elisp code after macroexpansion. | ||
| 715 | -- MLCVRS is a structure that contains captured and mutated variables. | ||
| 716 | (first MLCVRS) is a list of mutated variables, (second MLCVRS) is a | ||
| 717 | list of candidates for lambda lifting and (third MLCVRS) is a list of | ||
| 718 | variables captured by closure. It should be (nil nil nil) initially. | ||
| 719 | -- VARS is a list of local variables visible in current environment | ||
| 720 | (initially empty). | ||
| 721 | -- INCLOSURE is a boolean variable, true if we are in closure. | ||
| 722 | Initially false" | ||
| 723 | (pcase form | ||
| 724 | ; let special form | ||
| 725 | (`(,(and (or `let* `let) letsym) ,varsvalues . ,body-forms) | ||
| 726 | |||
| 727 | (when (eq letsym 'let) | ||
| 728 | (dolist (elm varsvalues) ; analyse values | ||
| 729 | (when (listp elm) | ||
| 730 | (cconv-analyse-form (cadr elm) vars inclosure)))) | ||
| 731 | |||
| 732 | (let ((v nil) | ||
| 733 | (var nil) | ||
| 734 | (value nil) | ||
| 735 | (varstruct nil)) | ||
| 736 | (dolist (elm varsvalues) | ||
| 737 | (if (listp elm) | ||
| 738 | (progn | ||
| 739 | (setq var (car elm)) | ||
| 740 | (setq value (cadr elm))) | ||
| 741 | (progn | ||
| 742 | (setq var elm) ; treat the form (let (x) ...) well | ||
| 743 | (setq value nil))) | ||
| 744 | |||
| 745 | (when (eq letsym 'let*) ; analyse value | ||
| 746 | (cconv-analyse-form value vars inclosure)) | ||
| 747 | |||
| 748 | (let (vars-new) ; remove the old var | ||
| 749 | (dolist (vr vars) | ||
| 750 | (when (not (eq (car vr) var)) | ||
| 751 | (push vr vars-new))) | ||
| 752 | (setq vars vars-new)) | ||
| 753 | |||
| 754 | (setq varstruct (list var inclosure elm form)) | ||
| 755 | (push varstruct vars) ; push a new one | ||
| 756 | |||
| 757 | (when (and (listp value) | ||
| 758 | (eq (car value) 'function) | ||
| 759 | (eq (caadr value) 'lambda)) | ||
| 760 | ; if var is a function | ||
| 761 | ; push it to lambda list | ||
| 762 | (push varstruct cconv-lambda-candidates)))) | ||
| 763 | |||
| 764 | (dolist (elm body-forms) ; analyse body forms | ||
| 765 | (cconv-analyse-form elm vars inclosure)) | ||
| 766 | nil) | ||
| 767 | ; defun special form | ||
| 768 | (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms) | ||
| 769 | (let ((v nil)) | ||
| 770 | (dolist (vr vrs) | ||
| 771 | (push (list vr form) vars))) ;push vrs to vars | ||
| 772 | (dolist (elm body-forms) ; analyse body forms | ||
| 773 | (cconv-analyse-form elm vars inclosure)) | ||
| 774 | nil) | ||
| 775 | |||
| 776 | (`(function . ((lambda ,vrs . ,body-forms))) | ||
| 777 | (if inclosure ;we are in closure | ||
| 778 | (setq inclosure (+ inclosure 1)) | ||
| 779 | (setq inclosure 1)) | ||
| 780 | (let (vars-new) ; update vars | ||
| 781 | (dolist (vr vars) ; we do that in such a tricky way | ||
| 782 | (when (not (memq (car vr) vrs)) ; to avoid side effects | ||
| 783 | (push vr vars-new))) | ||
| 784 | (dolist (vr vrs) | ||
| 785 | (push (list vr inclosure form) vars-new)) | ||
| 786 | (setq vars vars-new)) | ||
| 787 | |||
| 788 | (dolist (elm body-forms) | ||
| 789 | (cconv-analyse-form elm vars inclosure)) | ||
| 790 | nil) | ||
| 791 | |||
| 792 | (`(setq . ,forms) ; setq | ||
| 793 | ; if a local variable (member of vars) | ||
| 794 | ; is modified by setq | ||
| 795 | ; then it is a mutated variable | ||
| 796 | (while forms | ||
| 797 | (let ((v (assq (car forms) vars))) ; v = non nil if visible | ||
| 798 | (when v | ||
| 799 | (push v cconv-mutated) | ||
| 800 | ;; delete from candidate list for lambda lifting | ||
| 801 | (setq cconv-lambda-candidates (delq v cconv-lambda-candidates)) | ||
| 802 | (when inclosure | ||
| 803 | ;; test if v is declared as argument for lambda | ||
| 804 | (let* ((thirdv (third v)) | ||
| 805 | (isarg (if (listp thirdv) | ||
| 806 | (eq (car thirdv) 'function) nil))) | ||
| 807 | (if isarg | ||
| 808 | (when (> inclosure (cadr v)) ; when we are in closure | ||
| 809 | (push v cconv-captured)) ; push it to captured vars | ||
| 810 | ;; FIXME more detailed comments needed | ||
| 811 | (push v cconv-captured)))))) | ||
| 812 | (cconv-analyse-form (cadr forms) vars inclosure) | ||
| 813 | (setq forms (cddr forms))) | ||
| 814 | nil) | ||
| 815 | |||
| 816 | (`((lambda . ,_) . ,_) ; first element is lambda expression | ||
| 817 | (dolist (exp `((function ,(car form)) . ,(cdr form))) | ||
| 818 | (cconv-analyse-form exp vars inclosure)) | ||
| 819 | nil) | ||
| 820 | |||
| 821 | (`(cond . ,cond-forms) ; cond special form | ||
| 822 | (dolist (exp1 cond-forms) | ||
| 823 | (dolist (exp2 exp1) | ||
| 824 | (cconv-analyse-form exp2 vars inclosure))) | ||
| 825 | nil) | ||
| 826 | |||
| 827 | (`(quote . ,_) nil) ; quote form | ||
| 828 | |||
| 829 | (`(function . ,_) nil) ; same as quote | ||
| 830 | |||
| 831 | (`(condition-case ,var ,protected-form . ,conditions-bodies) | ||
| 832 | ;condition-case | ||
| 833 | (cconv-analyse-form protected-form vars inclosure) | ||
| 834 | (dolist (exp conditions-bodies) | ||
| 835 | (cconv-analyse-form (cadr exp) vars inclosure)) | ||
| 836 | nil) | ||
| 837 | |||
| 838 | (`(,(or `defconst `defvar `defsubst) ,value) | ||
| 839 | (cconv-analyse-form value vars inclosure)) | ||
| 840 | |||
| 841 | (`(,(or `funcall `apply) ,fun . ,args) | ||
| 842 | ;; Here we ignore fun because | ||
| 843 | ;; funcall and apply are the only two | ||
| 844 | ;; functions where we can pass a candidate | ||
| 845 | ;; for lambda lifting as argument. | ||
| 846 | ;; So, if we see fun elsewhere, we'll | ||
| 847 | ;; delete it from lambda candidate list. | ||
| 848 | |||
| 849 | ;; If this funcall and the definition of fun | ||
| 850 | ;; are in different closures - we delete fun from | ||
| 851 | ;; canidate list, because it is too complicated | ||
| 852 | ;; to manage free variables in this case. | ||
| 853 | (let ((lv (assq fun cconv-lambda-candidates))) | ||
| 854 | (when lv | ||
| 855 | (when (not (eq (cadr lv) inclosure)) | ||
| 856 | (setq cconv-lambda-candidates | ||
| 857 | (delq lv cconv-lambda-candidates))))) | ||
| 858 | |||
| 859 | (dolist (elm args) | ||
| 860 | (cconv-analyse-form elm vars inclosure)) | ||
| 861 | nil) | ||
| 862 | |||
| 863 | (`(,_ . ,body-forms) ; first element is a function or whatever | ||
| 864 | (dolist (exp body-forms) | ||
| 865 | (cconv-analyse-form exp vars inclosure)) | ||
| 866 | nil) | ||
| 867 | |||
| 868 | (_ | ||
| 869 | (when (and (symbolp form) | ||
| 870 | (not (memq form '(nil t))) | ||
| 871 | (not (keywordp form)) | ||
| 872 | (not (special-variable-p form))) | ||
| 873 | (let ((dv (assq form vars))) ; dv = declared and visible | ||
| 874 | (when dv | ||
| 875 | (when inclosure | ||
| 876 | ;; test if v is declared as argument of lambda | ||
| 877 | (let* ((thirddv (third dv)) | ||
| 878 | (isarg (if (listp thirddv) | ||
| 879 | (eq (car thirddv) 'function) nil))) | ||
| 880 | (if isarg | ||
| 881 | ;; FIXME add detailed comments | ||
| 882 | (when (> inclosure (cadr dv)) ; capturing condition | ||
| 883 | (push dv cconv-captured)) | ||
| 884 | (push dv cconv-captured)))) | ||
| 885 | ; delete lambda | ||
| 886 | (setq cconv-lambda-candidates ; if it is found here | ||
| 887 | (delq dv cconv-lambda-candidates))))) | ||
| 888 | nil))) | ||
| 889 | |||
| 890 | (provide 'cconv) | ||
| 891 | ;;; cconv.el ends here | ||
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index 24ea0a3e801..7990df264a9 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 1 | ;;; pcase.el --- ML-style pattern-matching macro for Elisp | 2 | ;;; pcase.el --- ML-style pattern-matching macro for Elisp |
| 2 | 3 | ||
| 3 | ;; Copyright (C) 2010-2011 Free Software Foundation, Inc. | 4 | ;; Copyright (C) 2010-2011 Free Software Foundation, Inc. |
| @@ -501,15 +502,14 @@ and otherwise defers to REST which is a list of branches of the form | |||
| 501 | ;; `(PAT3 . PAT4)) which the programmer can easily rewrite | 502 | ;; `(PAT3 . PAT4)) which the programmer can easily rewrite |
| 502 | ;; to the more efficient `(,(and PAT1 PAT3) . ,(and PAT2 PAT4))). | 503 | ;; to the more efficient `(,(and PAT1 PAT3) . ,(and PAT2 PAT4))). |
| 503 | (pcase--u1 `((match ,sym . ,(cadr upat))) | 504 | (pcase--u1 `((match ,sym . ,(cadr upat))) |
| 504 | (lexical-let ((rest rest)) | 505 | ;; FIXME: This codegen is not careful to share its |
| 505 | ;; FIXME: This codegen is not careful to share its | 506 | ;; code if used several times: code blow up is likely. |
| 506 | ;; code if used several times: code blow up is likely. | 507 | (lambda (vars) |
| 507 | (lambda (vars) | 508 | ;; `vars' will likely contain bindings which are |
| 508 | ;; `vars' will likely contain bindings which are | 509 | ;; not always available in other paths to |
| 509 | ;; not always available in other paths to | 510 | ;; `rest', so there' no point trying to pass |
| 510 | ;; `rest', so there' no point trying to pass | 511 | ;; them down. |
| 511 | ;; them down. | 512 | (pcase--u rest)) |
| 512 | (pcase--u rest))) | ||
| 513 | vars | 513 | vars |
| 514 | (list `((and . ,matches) ,code . ,vars)))) | 514 | (list `((and . ,matches) ,code . ,vars)))) |
| 515 | (t (error "Unknown upattern `%s'" upat))))) | 515 | (t (error "Unknown upattern `%s'" upat))))) |
diff --git a/lisp/mpc.el b/lisp/mpc.el index 8feddf8829b..4f21a162c08 100644 --- a/lisp/mpc.el +++ b/lisp/mpc.el | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 1 | ;;; mpc.el --- A client for the Music Player Daemon -*- coding: utf-8 -*- | 2 | ;;; mpc.el --- A client for the Music Player Daemon -*- coding: utf-8 -*- |
| 2 | 3 | ||
| 3 | ;; Copyright (C) 2006-2011 Free Software Foundation, Inc. | 4 | ;; Copyright (C) 2006-2011 Free Software Foundation, Inc. |
| @@ -341,9 +342,7 @@ CMD can be a string which is passed as-is to MPD or a list of strings | |||
| 341 | which will be concatenated with proper quoting before passing them to MPD." | 342 | which will be concatenated with proper quoting before passing them to MPD." |
| 342 | (let ((proc (mpc-proc))) | 343 | (let ((proc (mpc-proc))) |
| 343 | (if (and callback (not (process-get proc 'ready))) | 344 | (if (and callback (not (process-get proc 'ready))) |
| 344 | (lexical-let ((old (process-get proc 'callback)) | 345 | (let ((old (process-get proc 'callback))) |
| 345 | (callback callback) | ||
| 346 | (cmd cmd)) | ||
| 347 | (process-put proc 'callback | 346 | (process-put proc 'callback |
| 348 | (lambda () | 347 | (lambda () |
| 349 | (funcall old) | 348 | (funcall old) |
| @@ -359,8 +358,7 @@ which will be concatenated with proper quoting before passing them to MPD." | |||
| 359 | (mapconcat 'mpc--proc-quote-string cmd " ")) | 358 | (mapconcat 'mpc--proc-quote-string cmd " ")) |
| 360 | "\n"))) | 359 | "\n"))) |
| 361 | (if callback | 360 | (if callback |
| 362 | (lexical-let ((buf (current-buffer)) | 361 | (let ((buf (current-buffer))) |
| 363 | (callback callback)) | ||
| 364 | (process-put proc 'callback | 362 | (process-put proc 'callback |
| 365 | callback | 363 | callback |
| 366 | ;; (lambda () | 364 | ;; (lambda () |
| @@ -402,8 +400,7 @@ which will be concatenated with proper quoting before passing them to MPD." | |||
| 402 | 400 | ||
| 403 | (defun mpc-proc-cmd-to-alist (cmd &optional callback) | 401 | (defun mpc-proc-cmd-to-alist (cmd &optional callback) |
| 404 | (if callback | 402 | (if callback |
| 405 | (lexical-let ((buf (current-buffer)) | 403 | (let ((buf (current-buffer))) |
| 406 | (callback callback)) | ||
| 407 | (mpc-proc-cmd cmd (lambda () | 404 | (mpc-proc-cmd cmd (lambda () |
| 408 | (funcall callback (prog1 (mpc-proc-buf-to-alist | 405 | (funcall callback (prog1 (mpc-proc-buf-to-alist |
| 409 | (current-buffer)) | 406 | (current-buffer)) |
| @@ -522,7 +519,7 @@ to call FUN for any change whatsoever.") | |||
| 522 | 519 | ||
| 523 | (defun mpc-status-refresh (&optional callback) | 520 | (defun mpc-status-refresh (&optional callback) |
| 524 | "Refresh `mpc-status'." | 521 | "Refresh `mpc-status'." |
| 525 | (lexical-let ((cb callback)) | 522 | (let ((cb callback)) |
| 526 | (mpc-proc-cmd (mpc-proc-cmd-list '("status" "currentsong")) | 523 | (mpc-proc-cmd (mpc-proc-cmd-list '("status" "currentsong")) |
| 527 | (lambda () | 524 | (lambda () |
| 528 | (mpc--status-callback) | 525 | (mpc--status-callback) |
| @@ -775,7 +772,7 @@ The songs are returned as alists." | |||
| 775 | 772 | ||
| 776 | (defun mpc-cmd-pause (&optional arg callback) | 773 | (defun mpc-cmd-pause (&optional arg callback) |
| 777 | "Pause or resume playback of the queue of songs." | 774 | "Pause or resume playback of the queue of songs." |
| 778 | (lexical-let ((cb callback)) | 775 | (let ((cb callback)) |
| 779 | (mpc-proc-cmd (list "pause" arg) | 776 | (mpc-proc-cmd (list "pause" arg) |
| 780 | (lambda () (mpc-status-refresh) (if cb (funcall cb)))) | 777 | (lambda () (mpc-status-refresh) (if cb (funcall cb)))) |
| 781 | (unless callback (mpc-proc-sync)))) | 778 | (unless callback (mpc-proc-sync)))) |
| @@ -839,7 +836,7 @@ If PLAYLIST is t or nil or missing, use the main playlist." | |||
| 839 | (puthash (cons 'Playlist playlist) nil mpc--find-memoize)))) | 836 | (puthash (cons 'Playlist playlist) nil mpc--find-memoize)))) |
| 840 | 837 | ||
| 841 | (defun mpc-cmd-update (&optional arg callback) | 838 | (defun mpc-cmd-update (&optional arg callback) |
| 842 | (lexical-let ((cb callback)) | 839 | (let ((cb callback)) |
| 843 | (mpc-proc-cmd (if arg (list "update" arg) "update") | 840 | (mpc-proc-cmd (if arg (list "update" arg) "update") |
| 844 | (lambda () (mpc-status-refresh) (if cb (funcall cb)))) | 841 | (lambda () (mpc-status-refresh) (if cb (funcall cb)))) |
| 845 | (unless callback (mpc-proc-sync)))) | 842 | (unless callback (mpc-proc-sync)))) |
| @@ -2351,8 +2348,7 @@ This is used so that they can be compared with `eq', which is needed for | |||
| 2351 | (mpc-proc-cmd (list "seekid" songid time) | 2348 | (mpc-proc-cmd (list "seekid" songid time) |
| 2352 | 'mpc-status-refresh)))) | 2349 | 'mpc-status-refresh)))) |
| 2353 | (let ((status (mpc-cmd-status))) | 2350 | (let ((status (mpc-cmd-status))) |
| 2354 | (lexical-let* ((songid (cdr (assq 'songid status))) | 2351 | (let* ((songid (cdr (assq 'songid status))) |
| 2355 | (step step) | ||
| 2356 | (time (if songid (string-to-number | 2352 | (time (if songid (string-to-number |
| 2357 | (cdr (assq 'time status)))))) | 2353 | (cdr (assq 'time status)))))) |
| 2358 | (let ((timer (run-with-timer | 2354 | (let ((timer (run-with-timer |
| @@ -2389,13 +2385,12 @@ This is used so that they can be compared with `eq', which is needed for | |||
| 2389 | (if mpc--faster-toggle-timer | 2385 | (if mpc--faster-toggle-timer |
| 2390 | (mpc--faster-stop) | 2386 | (mpc--faster-stop) |
| 2391 | (mpc-status-refresh) (mpc-proc-sync) | 2387 | (mpc-status-refresh) (mpc-proc-sync) |
| 2392 | (lexical-let* ((speedup speedup) | 2388 | (let* (songid ;The ID of the currently ffwd/rewinding song. |
| 2393 | songid ;The ID of the currently ffwd/rewinding song. | 2389 | songnb ;The position of that song in the playlist. |
| 2394 | songnb ;The position of that song in the playlist. | 2390 | songduration ;The duration of that song. |
| 2395 | songduration ;The duration of that song. | 2391 | songtime ;The time of the song last time we ran. |
| 2396 | songtime ;The time of the song last time we ran. | 2392 | oldtime ;The timeoftheday last time we ran. |
| 2397 | oldtime ;The timeoftheday last time we ran. | 2393 | prevsongid) ;The song we're in the process leaving. |
| 2398 | prevsongid) ;The song we're in the process leaving. | ||
| 2399 | (let ((fun | 2394 | (let ((fun |
| 2400 | (lambda () | 2395 | (lambda () |
| 2401 | (let ((newsongid (cdr (assq 'songid mpc-status))) | 2396 | (let ((newsongid (cdr (assq 'songid mpc-status))) |
diff --git a/lisp/server.el b/lisp/server.el index 62c59b41cee..1ee30f5bc3c 100644 --- a/lisp/server.el +++ b/lisp/server.el | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | ;;; -*- lexical-binding: t -*- | ||
| 1 | ;;; server.el --- Lisp code for GNU Emacs running as server process | 2 | ;;; server.el --- Lisp code for GNU Emacs running as server process |
| 2 | 3 | ||
| 3 | ;; Copyright (C) 1986-1987, 1992, 1994-2011 Free Software Foundation, Inc. | 4 | ;; Copyright (C) 1986-1987, 1992, 1994-2011 Free Software Foundation, Inc. |
| @@ -335,9 +336,9 @@ If CLIENT is non-nil, add a description of it to the logged message." | |||
| 335 | (goto-char (point-max)) | 336 | (goto-char (point-max)) |
| 336 | (insert (funcall server-log-time-function) | 337 | (insert (funcall server-log-time-function) |
| 337 | (cond | 338 | (cond |
| 338 | ((null client) " ") | 339 | ((null client) " ") |
| 339 | ((listp client) (format " %s: " (car client))) | 340 | ((listp client) (format " %s: " (car client))) |
| 340 | (t (format " %s: " client))) | 341 | (t (format " %s: " client))) |
| 341 | string) | 342 | string) |
| 342 | (or (bolp) (newline))))) | 343 | (or (bolp) (newline))))) |
| 343 | 344 | ||
| @@ -355,7 +356,7 @@ If CLIENT is non-nil, add a description of it to the logged message." | |||
| 355 | (and (process-contact proc :server) | 356 | (and (process-contact proc :server) |
| 356 | (eq (process-status proc) 'closed) | 357 | (eq (process-status proc) 'closed) |
| 357 | (ignore-errors | 358 | (ignore-errors |
| 358 | (delete-file (process-get proc :server-file)))) | 359 | (delete-file (process-get proc :server-file)))) |
| 359 | (server-log (format "Status changed to %s: %s" (process-status proc) msg) proc) | 360 | (server-log (format "Status changed to %s: %s" (process-status proc) msg) proc) |
| 360 | (server-delete-client proc)) | 361 | (server-delete-client proc)) |
| 361 | 362 | ||
| @@ -410,10 +411,10 @@ If CLIENT is non-nil, add a description of it to the logged message." | |||
| 410 | proc | 411 | proc |
| 411 | ;; See if this is the last frame for this client. | 412 | ;; See if this is the last frame for this client. |
| 412 | (>= 1 (let ((frame-num 0)) | 413 | (>= 1 (let ((frame-num 0)) |
| 413 | (dolist (f (frame-list)) | 414 | (dolist (f (frame-list)) |
| 414 | (when (eq proc (frame-parameter f 'client)) | 415 | (when (eq proc (frame-parameter f 'client)) |
| 415 | (setq frame-num (1+ frame-num)))) | 416 | (setq frame-num (1+ frame-num)))) |
| 416 | frame-num))) | 417 | frame-num))) |
| 417 | (server-log (format "server-handle-delete-frame, frame %s" frame) proc) | 418 | (server-log (format "server-handle-delete-frame, frame %s" frame) proc) |
| 418 | (server-delete-client proc 'noframe)))) ; Let delete-frame delete the frame later. | 419 | (server-delete-client proc 'noframe)))) ; Let delete-frame delete the frame later. |
| 419 | 420 | ||
| @@ -534,8 +535,8 @@ To force-start a server, do \\[server-force-delete] and then | |||
| 534 | (if (not (eq t (server-running-p server-name))) | 535 | (if (not (eq t (server-running-p server-name))) |
| 535 | ;; Remove any leftover socket or authentication file | 536 | ;; Remove any leftover socket or authentication file |
| 536 | (ignore-errors | 537 | (ignore-errors |
| 537 | (let (delete-by-moving-to-trash) | 538 | (let (delete-by-moving-to-trash) |
| 538 | (delete-file server-file))) | 539 | (delete-file server-file))) |
| 539 | (setq server-mode nil) ;; already set by the minor mode code | 540 | (setq server-mode nil) ;; already set by the minor mode code |
| 540 | (display-warning | 541 | (display-warning |
| 541 | 'server | 542 | 'server |
| @@ -590,11 +591,11 @@ server or call `M-x server-force-delete' to forcibly disconnect it.") | |||
| 590 | (when server-use-tcp | 591 | (when server-use-tcp |
| 591 | (let ((auth-key | 592 | (let ((auth-key |
| 592 | (loop | 593 | (loop |
| 593 | ;; The auth key is a 64-byte string of random chars in the | 594 | ;; The auth key is a 64-byte string of random chars in the |
| 594 | ;; range `!'..`~'. | 595 | ;; range `!'..`~'. |
| 595 | repeat 64 | 596 | repeat 64 |
| 596 | collect (+ 33 (random 94)) into auth | 597 | collect (+ 33 (random 94)) into auth |
| 597 | finally return (concat auth)))) | 598 | finally return (concat auth)))) |
| 598 | (process-put server-process :auth-key auth-key) | 599 | (process-put server-process :auth-key auth-key) |
| 599 | (with-temp-file server-file | 600 | (with-temp-file server-file |
| 600 | (set-buffer-multibyte nil) | 601 | (set-buffer-multibyte nil) |
| @@ -689,31 +690,31 @@ Server mode runs a process that accepts commands from the | |||
| 689 | (add-to-list 'frame-inherited-parameters 'client) | 690 | (add-to-list 'frame-inherited-parameters 'client) |
| 690 | (let ((frame | 691 | (let ((frame |
| 691 | (server-with-environment (process-get proc 'env) | 692 | (server-with-environment (process-get proc 'env) |
| 692 | '("LANG" "LC_CTYPE" "LC_ALL" | 693 | '("LANG" "LC_CTYPE" "LC_ALL" |
| 693 | ;; For tgetent(3); list according to ncurses(3). | 694 | ;; For tgetent(3); list according to ncurses(3). |
| 694 | "BAUDRATE" "COLUMNS" "ESCDELAY" "HOME" "LINES" | 695 | "BAUDRATE" "COLUMNS" "ESCDELAY" "HOME" "LINES" |
| 695 | "NCURSES_ASSUMED_COLORS" "NCURSES_NO_PADDING" | 696 | "NCURSES_ASSUMED_COLORS" "NCURSES_NO_PADDING" |
| 696 | "NCURSES_NO_SETBUF" "TERM" "TERMCAP" "TERMINFO" | 697 | "NCURSES_NO_SETBUF" "TERM" "TERMCAP" "TERMINFO" |
| 697 | "TERMINFO_DIRS" "TERMPATH" | 698 | "TERMINFO_DIRS" "TERMPATH" |
| 698 | ;; rxvt wants these | 699 | ;; rxvt wants these |
| 699 | "COLORFGBG" "COLORTERM") | 700 | "COLORFGBG" "COLORTERM") |
| 700 | (make-frame `((window-system . nil) | 701 | (make-frame `((window-system . nil) |
| 701 | (tty . ,tty) | 702 | (tty . ,tty) |
| 702 | (tty-type . ,type) | 703 | (tty-type . ,type) |
| 703 | ;; Ignore nowait here; we always need to | 704 | ;; Ignore nowait here; we always need to |
| 704 | ;; clean up opened ttys when the client dies. | 705 | ;; clean up opened ttys when the client dies. |
| 705 | (client . ,proc) | 706 | (client . ,proc) |
| 706 | ;; This is a leftover from an earlier | 707 | ;; This is a leftover from an earlier |
| 707 | ;; attempt at making it possible for process | 708 | ;; attempt at making it possible for process |
| 708 | ;; run in the server process to use the | 709 | ;; run in the server process to use the |
| 709 | ;; environment of the client process. | 710 | ;; environment of the client process. |
| 710 | ;; It has no effect now and to make it work | 711 | ;; It has no effect now and to make it work |
| 711 | ;; we'd need to decide how to make | 712 | ;; we'd need to decide how to make |
| 712 | ;; process-environment interact with client | 713 | ;; process-environment interact with client |
| 713 | ;; envvars, and then to change the | 714 | ;; envvars, and then to change the |
| 714 | ;; C functions `child_setup' and | 715 | ;; C functions `child_setup' and |
| 715 | ;; `getenv_internal' accordingly. | 716 | ;; `getenv_internal' accordingly. |
| 716 | (environment . ,(process-get proc 'env))))))) | 717 | (environment . ,(process-get proc 'env))))))) |
| 717 | 718 | ||
| 718 | ;; ttys don't use the `display' parameter, but callproc.c does to set | 719 | ;; ttys don't use the `display' parameter, but callproc.c does to set |
| 719 | ;; the DISPLAY environment on subprocesses. | 720 | ;; the DISPLAY environment on subprocesses. |
| @@ -777,8 +778,7 @@ Server mode runs a process that accepts commands from the | |||
| 777 | ;; frame because input from that display will be blocked (until exiting | 778 | ;; frame because input from that display will be blocked (until exiting |
| 778 | ;; the minibuffer). Better exit this minibuffer right away. | 779 | ;; the minibuffer). Better exit this minibuffer right away. |
| 779 | ;; Similarly with recursive-edits such as the splash screen. | 780 | ;; Similarly with recursive-edits such as the splash screen. |
| 780 | (run-with-timer 0 nil (lexical-let ((proc proc)) | 781 | (run-with-timer 0 nil (lambda () (server-execute-continuation proc))) |
| 781 | (lambda () (server-execute-continuation proc)))) | ||
| 782 | (top-level))) | 782 | (top-level))) |
| 783 | 783 | ||
| 784 | ;; We use various special properties on process objects: | 784 | ;; We use various special properties on process objects: |
| @@ -944,119 +944,119 @@ The following commands are accepted by the client: | |||
| 944 | (setq command-line-args-left | 944 | (setq command-line-args-left |
| 945 | (mapcar 'server-unquote-arg (split-string request " " t))) | 945 | (mapcar 'server-unquote-arg (split-string request " " t))) |
| 946 | (while (setq arg (pop command-line-args-left)) | 946 | (while (setq arg (pop command-line-args-left)) |
| 947 | (cond | 947 | (cond |
| 948 | ;; -version CLIENT-VERSION: obsolete at birth. | 948 | ;; -version CLIENT-VERSION: obsolete at birth. |
| 949 | ((and (equal "-version" arg) command-line-args-left) | 949 | ((and (equal "-version" arg) command-line-args-left) |
| 950 | (pop command-line-args-left)) | 950 | (pop command-line-args-left)) |
| 951 | 951 | ||
| 952 | ;; -nowait: Emacsclient won't wait for a result. | 952 | ;; -nowait: Emacsclient won't wait for a result. |
| 953 | ((equal "-nowait" arg) (setq nowait t)) | 953 | ((equal "-nowait" arg) (setq nowait t)) |
| 954 | 954 | ||
| 955 | ;; -current-frame: Don't create frames. | 955 | ;; -current-frame: Don't create frames. |
| 956 | ((equal "-current-frame" arg) (setq use-current-frame t)) | 956 | ((equal "-current-frame" arg) (setq use-current-frame t)) |
| 957 | 957 | ||
| 958 | ;; -display DISPLAY: | 958 | ;; -display DISPLAY: |
| 959 | ;; Open X frames on the given display instead of the default. | 959 | ;; Open X frames on the given display instead of the default. |
| 960 | ((and (equal "-display" arg) command-line-args-left) | 960 | ((and (equal "-display" arg) command-line-args-left) |
| 961 | (setq display (pop command-line-args-left)) | 961 | (setq display (pop command-line-args-left)) |
| 962 | (if (zerop (length display)) (setq display nil))) | 962 | (if (zerop (length display)) (setq display nil))) |
| 963 | 963 | ||
| 964 | ;; -parent-id ID: | 964 | ;; -parent-id ID: |
| 965 | ;; Open X frame within window ID, via XEmbed. | 965 | ;; Open X frame within window ID, via XEmbed. |
| 966 | ((and (equal "-parent-id" arg) command-line-args-left) | 966 | ((and (equal "-parent-id" arg) command-line-args-left) |
| 967 | (setq parent-id (pop command-line-args-left)) | 967 | (setq parent-id (pop command-line-args-left)) |
| 968 | (if (zerop (length parent-id)) (setq parent-id nil))) | 968 | (if (zerop (length parent-id)) (setq parent-id nil))) |
| 969 | 969 | ||
| 970 | ;; -window-system: Open a new X frame. | 970 | ;; -window-system: Open a new X frame. |
| 971 | ((equal "-window-system" arg) | 971 | ((equal "-window-system" arg) |
| 972 | (setq dontkill t) | 972 | (setq dontkill t) |
| 973 | (setq tty-name 'window-system)) | 973 | (setq tty-name 'window-system)) |
| 974 | 974 | ||
| 975 | ;; -resume: Resume a suspended tty frame. | 975 | ;; -resume: Resume a suspended tty frame. |
| 976 | ((equal "-resume" arg) | 976 | ((equal "-resume" arg) |
| 977 | (lexical-let ((terminal (process-get proc 'terminal))) | 977 | (let ((terminal (process-get proc 'terminal))) |
| 978 | (setq dontkill t) | 978 | (setq dontkill t) |
| 979 | (push (lambda () | 979 | (push (lambda () |
| 980 | (when (eq (terminal-live-p terminal) t) | 980 | (when (eq (terminal-live-p terminal) t) |
| 981 | (resume-tty terminal))) | 981 | (resume-tty terminal))) |
| 982 | commands))) | 982 | commands))) |
| 983 | 983 | ||
| 984 | ;; -suspend: Suspend the client's frame. (In case we | 984 | ;; -suspend: Suspend the client's frame. (In case we |
| 985 | ;; get out of sync, and a C-z sends a SIGTSTP to | 985 | ;; get out of sync, and a C-z sends a SIGTSTP to |
| 986 | ;; emacsclient.) | 986 | ;; emacsclient.) |
| 987 | ((equal "-suspend" arg) | 987 | ((equal "-suspend" arg) |
| 988 | (lexical-let ((terminal (process-get proc 'terminal))) | 988 | (let ((terminal (process-get proc 'terminal))) |
| 989 | (setq dontkill t) | ||
| 990 | (push (lambda () | ||
| 991 | (when (eq (terminal-live-p terminal) t) | ||
| 992 | (suspend-tty terminal))) | ||
| 993 | commands))) | ||
| 994 | |||
| 995 | ;; -ignore COMMENT: Noop; useful for debugging emacsclient. | ||
| 996 | ;; (The given comment appears in the server log.) | ||
| 997 | ((and (equal "-ignore" arg) command-line-args-left | ||
| 998 | (setq dontkill t) | 989 | (setq dontkill t) |
| 999 | (pop command-line-args-left))) | 990 | (push (lambda () |
| 1000 | 991 | (when (eq (terminal-live-p terminal) t) | |
| 1001 | ;; -tty DEVICE-NAME TYPE: Open a new tty frame at the client. | 992 | (suspend-tty terminal))) |
| 1002 | ((and (equal "-tty" arg) | 993 | commands))) |
| 1003 | (cdr command-line-args-left)) | 994 | |
| 1004 | (setq tty-name (pop command-line-args-left) | 995 | ;; -ignore COMMENT: Noop; useful for debugging emacsclient. |
| 1005 | tty-type (pop command-line-args-left) | 996 | ;; (The given comment appears in the server log.) |
| 1006 | dontkill (or dontkill | 997 | ((and (equal "-ignore" arg) command-line-args-left |
| 1007 | (not use-current-frame)))) | 998 | (setq dontkill t) |
| 1008 | 999 | (pop command-line-args-left))) | |
| 1009 | ;; -position LINE[:COLUMN]: Set point to the given | 1000 | |
| 1010 | ;; position in the next file. | 1001 | ;; -tty DEVICE-NAME TYPE: Open a new tty frame at the client. |
| 1011 | ((and (equal "-position" arg) | 1002 | ((and (equal "-tty" arg) |
| 1012 | command-line-args-left | 1003 | (cdr command-line-args-left)) |
| 1013 | (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?" | 1004 | (setq tty-name (pop command-line-args-left) |
| 1014 | (car command-line-args-left))) | 1005 | tty-type (pop command-line-args-left) |
| 1015 | (setq arg (pop command-line-args-left)) | 1006 | dontkill (or dontkill |
| 1016 | (setq filepos | 1007 | (not use-current-frame)))) |
| 1017 | (cons (string-to-number (match-string 1 arg)) | 1008 | |
| 1018 | (string-to-number (or (match-string 2 arg) ""))))) | 1009 | ;; -position LINE[:COLUMN]: Set point to the given |
| 1019 | 1010 | ;; position in the next file. | |
| 1020 | ;; -file FILENAME: Load the given file. | 1011 | ((and (equal "-position" arg) |
| 1021 | ((and (equal "-file" arg) | 1012 | command-line-args-left |
| 1022 | command-line-args-left) | 1013 | (string-match "\\+\\([0-9]+\\)\\(?::\\([0-9]+\\)\\)?" |
| 1023 | (let ((file (pop command-line-args-left))) | 1014 | (car command-line-args-left))) |
| 1024 | (if coding-system | 1015 | (setq arg (pop command-line-args-left)) |
| 1025 | (setq file (decode-coding-string file coding-system))) | 1016 | (setq filepos |
| 1026 | (setq file (expand-file-name file dir)) | 1017 | (cons (string-to-number (match-string 1 arg)) |
| 1027 | (push (cons file filepos) files) | 1018 | (string-to-number (or (match-string 2 arg) ""))))) |
| 1028 | (server-log (format "New file: %s %s" | 1019 | |
| 1029 | file (or filepos "")) proc)) | 1020 | ;; -file FILENAME: Load the given file. |
| 1030 | (setq filepos nil)) | 1021 | ((and (equal "-file" arg) |
| 1031 | 1022 | command-line-args-left) | |
| 1032 | ;; -eval EXPR: Evaluate a Lisp expression. | 1023 | (let ((file (pop command-line-args-left))) |
| 1033 | ((and (equal "-eval" arg) | ||
| 1034 | command-line-args-left) | ||
| 1035 | (if use-current-frame | ||
| 1036 | (setq use-current-frame 'always)) | ||
| 1037 | (lexical-let ((expr (pop command-line-args-left))) | ||
| 1038 | (if coding-system | ||
| 1039 | (setq expr (decode-coding-string expr coding-system))) | ||
| 1040 | (push (lambda () (server-eval-and-print expr proc)) | ||
| 1041 | commands) | ||
| 1042 | (setq filepos nil))) | ||
| 1043 | |||
| 1044 | ;; -env NAME=VALUE: An environment variable. | ||
| 1045 | ((and (equal "-env" arg) command-line-args-left) | ||
| 1046 | (let ((var (pop command-line-args-left))) | ||
| 1047 | ;; XXX Variables should be encoded as in getenv/setenv. | ||
| 1048 | (process-put proc 'env | ||
| 1049 | (cons var (process-get proc 'env))))) | ||
| 1050 | |||
| 1051 | ;; -dir DIRNAME: The cwd of the emacsclient process. | ||
| 1052 | ((and (equal "-dir" arg) command-line-args-left) | ||
| 1053 | (setq dir (pop command-line-args-left)) | ||
| 1054 | (if coding-system | 1024 | (if coding-system |
| 1055 | (setq dir (decode-coding-string dir coding-system))) | 1025 | (setq file (decode-coding-string file coding-system))) |
| 1056 | (setq dir (command-line-normalize-file-name dir))) | 1026 | (setq file (expand-file-name file dir)) |
| 1057 | 1027 | (push (cons file filepos) files) | |
| 1058 | ;; Unknown command. | 1028 | (server-log (format "New file: %s %s" |
| 1059 | (t (error "Unknown command: %s" arg)))) | 1029 | file (or filepos "")) proc)) |
| 1030 | (setq filepos nil)) | ||
| 1031 | |||
| 1032 | ;; -eval EXPR: Evaluate a Lisp expression. | ||
| 1033 | ((and (equal "-eval" arg) | ||
| 1034 | command-line-args-left) | ||
| 1035 | (if use-current-frame | ||
| 1036 | (setq use-current-frame 'always)) | ||
| 1037 | (let ((expr (pop command-line-args-left))) | ||
| 1038 | (if coding-system | ||
| 1039 | (setq expr (decode-coding-string expr coding-system))) | ||
| 1040 | (push (lambda () (server-eval-and-print expr proc)) | ||
| 1041 | commands) | ||
| 1042 | (setq filepos nil))) | ||
| 1043 | |||
| 1044 | ;; -env NAME=VALUE: An environment variable. | ||
| 1045 | ((and (equal "-env" arg) command-line-args-left) | ||
| 1046 | (let ((var (pop command-line-args-left))) | ||
| 1047 | ;; XXX Variables should be encoded as in getenv/setenv. | ||
| 1048 | (process-put proc 'env | ||
| 1049 | (cons var (process-get proc 'env))))) | ||
| 1050 | |||
| 1051 | ;; -dir DIRNAME: The cwd of the emacsclient process. | ||
| 1052 | ((and (equal "-dir" arg) command-line-args-left) | ||
| 1053 | (setq dir (pop command-line-args-left)) | ||
| 1054 | (if coding-system | ||
| 1055 | (setq dir (decode-coding-string dir coding-system))) | ||
| 1056 | (setq dir (command-line-normalize-file-name dir))) | ||
| 1057 | |||
| 1058 | ;; Unknown command. | ||
| 1059 | (t (error "Unknown command: %s" arg)))) | ||
| 1060 | 1060 | ||
| 1061 | (setq frame | 1061 | (setq frame |
| 1062 | (cond | 1062 | (cond |
| @@ -1079,23 +1079,15 @@ The following commands are accepted by the client: | |||
| 1079 | 1079 | ||
| 1080 | (process-put | 1080 | (process-put |
| 1081 | proc 'continuation | 1081 | proc 'continuation |
| 1082 | (lexical-let ((proc proc) | 1082 | (lambda () |
| 1083 | (files files) | 1083 | (with-current-buffer (get-buffer-create server-buffer) |
| 1084 | (nowait nowait) | 1084 | ;; Use the same cwd as the emacsclient, if possible, so |
| 1085 | (commands commands) | 1085 | ;; relative file names work correctly, even in `eval'. |
| 1086 | (dontkill dontkill) | 1086 | (let ((default-directory |
| 1087 | (frame frame) | 1087 | (if (and dir (file-directory-p dir)) |
| 1088 | (dir dir) | 1088 | dir default-directory))) |
| 1089 | (tty-name tty-name)) | 1089 | (server-execute proc files nowait commands |
| 1090 | (lambda () | 1090 | dontkill frame tty-name))))) |
| 1091 | (with-current-buffer (get-buffer-create server-buffer) | ||
| 1092 | ;; Use the same cwd as the emacsclient, if possible, so | ||
| 1093 | ;; relative file names work correctly, even in `eval'. | ||
| 1094 | (let ((default-directory | ||
| 1095 | (if (and dir (file-directory-p dir)) | ||
| 1096 | dir default-directory))) | ||
| 1097 | (server-execute proc files nowait commands | ||
| 1098 | dontkill frame tty-name)))))) | ||
| 1099 | 1091 | ||
| 1100 | (when (or frame files) | 1092 | (when (or frame files) |
| 1101 | (server-goto-toplevel proc)) | 1093 | (server-goto-toplevel proc)) |
| @@ -1372,12 +1364,12 @@ If invoked with a prefix argument, or if there is no server process running, | |||
| 1372 | starts server process and that is all. Invoked by \\[server-edit]." | 1364 | starts server process and that is all. Invoked by \\[server-edit]." |
| 1373 | (interactive "P") | 1365 | (interactive "P") |
| 1374 | (cond | 1366 | (cond |
| 1375 | ((or arg | 1367 | ((or arg |
| 1376 | (not server-process) | 1368 | (not server-process) |
| 1377 | (memq (process-status server-process) '(signal exit))) | 1369 | (memq (process-status server-process) '(signal exit))) |
| 1378 | (server-mode 1)) | 1370 | (server-mode 1)) |
| 1379 | (server-clients (apply 'server-switch-buffer (server-done))) | 1371 | (server-clients (apply 'server-switch-buffer (server-done))) |
| 1380 | (t (message "No server editing buffers exist")))) | 1372 | (t (message "No server editing buffers exist")))) |
| 1381 | 1373 | ||
| 1382 | (defun server-switch-buffer (&optional next-buffer killed-one filepos) | 1374 | (defun server-switch-buffer (&optional next-buffer killed-one filepos) |
| 1383 | "Switch to another buffer, preferably one that has a client. | 1375 | "Switch to another buffer, preferably one that has a client. |