* More use of lexical-binding in ox.el
@ 2021-04-20 3:37 Stefan Monnier
2021-04-27 21:12 ` Nicolas Goaziou
0 siblings, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2021-04-20 3:37 UTC (permalink / raw)
To: Nicolas Goaziou; +Cc: emacs-orgmode
Here's another patch to remove some more use of the old dynamically
scoped dialect of ELisp.
Stefan
* lisp/ox.el: Fix various uses of the non-lexical-binding ELisp dialect.
(org-export--get-global-options, org-export-insert-default-template):
Use lexical-binding.
(org-export--generate-copy-script): Return a closure rather than
list starting with `lambda`.
(org-export-async-start): Turn it into a function (there seems to be
no reason this was a macro). Use `write-region` rather than
`with-temp-file`. Always use `utf-8-emacs-unix` coding system since
it's more efficient and is guaranteed to handle all chars.
Use lexical-binding in the temp file as well.
Actually set `debug-on-error` if `org-export-async-debug` says so.
(org-export-to-buffer, org-export-to-file): Pass a closure rather than
list starting with `lambda` to `org-export-async-start`.
diff --git a/lisp/ox.el b/lisp/ox.el
index 758b9370b3..2ce8985a9e 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -1571,7 +1571,7 @@ process."
plist
prop
;; Evaluate default value provided.
- (let ((value (eval (nth 3 cell))))
+ (let ((value (eval (nth 3 cell) t)))
(if (eq (nth 4 cell) 'parse)
(org-element-parse-secondary-string
value (org-element-restriction 'keyword))
@@ -2561,51 +2561,59 @@ another buffer, effectively cloning the original buffer there.
The function assumes BUFFER's major mode is `org-mode'."
(with-current-buffer buffer
- `(lambda ()
- (let ((inhibit-modification-hooks t))
- ;; Set major mode. Ignore `org-mode-hook' as it has been run
- ;; already in BUFFER.
- (let ((org-mode-hook nil) (org-inhibit-startup t)) (org-mode))
- ;; Copy specific buffer local variables and variables set
- ;; through BIND keywords.
- ,@(let ((bound-variables (org-export--list-bound-variables))
- vars)
- (dolist (entry (buffer-local-variables (buffer-base-buffer)) vars)
+ (let ((str (org-with-wide-buffer (buffer-string)))
+ (narrowing
+ (if (org-region-active-p)
+ (list (region-beginning) (region-end))
+ (list (point-min) (point-max))))
+ (pos (point))
+ (varvals
+ (let ((bound-variables (org-export--list-bound-variables))
+ varvals)
+ (dolist (entry (buffer-local-variables (buffer-base-buffer)))
(when (consp entry)
(let ((var (car entry))
(val (cdr entry)))
(and (not (memq var org-export-ignored-local-variables))
(or (memq var
'(default-directory
- buffer-file-name
- buffer-file-coding-system))
+ buffer-file-name
+ buffer-file-coding-system))
(assq var bound-variables)
(string-match "^\\(org-\\|orgtbl-\\)"
(symbol-name var)))
;; Skip unreadable values, as they cannot be
;; sent to external process.
(or (not val) (ignore-errors (read (format "%S" val))))
- (push `(set (make-local-variable (quote ,var))
- (quote ,val))
- vars))))))
- ;; Whole buffer contents.
- (insert ,(org-with-wide-buffer (buffer-string)))
- ;; Narrowing.
- ,(if (org-region-active-p)
- `(narrow-to-region ,(region-beginning) ,(region-end))
- `(narrow-to-region ,(point-min) ,(point-max)))
- ;; Current position of point.
- (goto-char ,(point))
- ;; Overlays with invisible property.
- ,@(let (ov-set)
- (dolist (ov (overlays-in (point-min) (point-max)) ov-set)
+ (push (cons var val) varvals))))
+ varvals)))
+ (ols
+ (let (ov-set)
+ (dolist (ov (overlays-in (point-min) (point-max)))
(let ((invis-prop (overlay-get ov 'invisible)))
(when invis-prop
- (push `(overlay-put
- (make-overlay ,(overlay-start ov)
- ,(overlay-end ov))
- 'invisible (quote ,invis-prop))
- ov-set)))))))))
+ (push (list (overlay-start ov) (overlay-end ov)
+ invis-prop)
+ ov-set))))
+ ov-set)))
+ (lambda ()
+ (let ((inhibit-modification-hooks t))
+ ;; Set major mode. Ignore `org-mode-hook' as it has been run
+ ;; already in BUFFER.
+ (let ((org-mode-hook nil) (org-inhibit-startup t)) (org-mode))
+ ;; Copy specific buffer local variables and variables set
+ ;; through BIND keywords.
+ (pcase-dolist (`(,var . ,val) varvals)
+ (set (make-local-variable var) val))
+ ;; Whole buffer contents.
+ (insert str)
+ ;; Narrowing.
+ (apply #'narrow-to-region narrowing)
+ ;; Current position of point.
+ (goto-char pos)
+ ;; Overlays with invisible property.
+ (pcase-dolist (`(,start ,end ,invis) ols)
+ (overlay-put (make-overlay start end) 'invisible invis)))))))
(defun org-export--delete-comment-trees ()
"Delete commented trees and commented inlinetasks in the buffer.
@@ -3104,11 +3112,11 @@ locally for the subtree through node properties."
(keyword (unless (assoc keyword keywords)
(let ((value
(if (eq (nth 4 entry) 'split)
- (mapconcat #'identity (eval (nth 3 entry)) " ")
- (eval (nth 3 entry)))))
+ (mapconcat #'identity (eval (nth 3 entry) t) " ")
+ (eval (nth 3 entry) t))))
(push (cons keyword value) keywords))))
(option (unless (assoc option options)
- (push (cons option (eval (nth 3 entry))) options))))))
+ (push (cons option (eval (nth 3 entry) t)) options))))))
;; Move to an appropriate location in order to insert options.
(unless subtreep (beginning-of-line))
;; First (multiple) OPTIONS lines. Never go past fill-column.
@@ -3119,7 +3127,7 @@ locally for the subtree through node properties."
(sort options (lambda (k1 k2) (string< (car k1) (car k2)))))))
(if subtreep
(org-entry-put
- node "EXPORT_OPTIONS" (mapconcat 'identity items " "))
+ node "EXPORT_OPTIONS" (mapconcat #'identity items " "))
(while items
(insert "#+options:")
(let ((width 10))
@@ -3609,7 +3617,7 @@ will become the empty string."
(attributes
(let ((value (org-element-property attribute element)))
(when value
- (let ((s (mapconcat 'identity value " ")) result)
+ (let ((s (mapconcat #'identity value " ")) result)
(while (string-match
"\\(?:^\\|[ \t]+\\)\\(:[-a-zA-Z0-9_]+\\)\\([ \t]+\\|$\\)"
s)
@@ -4702,7 +4710,7 @@ code."
;; should start six columns after the widest line of code,
;; wrapped with parenthesis.
(max-width
- (+ (apply 'max (mapcar 'length code-lines))
+ (+ (apply #'max (mapcar #'length code-lines))
(if (not num-start) 0 (length (format num-fmt num-start))))))
(org-export-format-code
code
@@ -6200,91 +6208,87 @@ to `:default' encoding. If it fails, return S."
;; For back-ends, `org-export-add-to-stack' add a new source to stack.
;; It should be used whenever `org-export-async-start' is called.
-(defmacro org-export-async-start (fun &rest body)
+(defun org-export-async-start (fun body)
"Call function FUN on the results returned by BODY evaluation.
-FUN is an anonymous function of one argument. BODY evaluation
-happens in an asynchronous process, from a buffer which is an
-exact copy of the current one.
+FUN is an anonymous function of one argument. BODY should be a valid
+ELisp source expression. BODY evaluation happens in an asynchronous process,
+from a buffer which is an exact copy of the current one.
Use `org-export-add-to-stack' in FUN in order to register results
in the stack.
This is a low level function. See also `org-export-to-buffer'
and `org-export-to-file' for more specialized functions."
- (declare (indent 1) (debug t))
- (org-with-gensyms (process temp-file copy-fun proc-buffer coding)
- ;; Write the full sexp evaluating BODY in a copy of the current
- ;; buffer to a temporary file, as it may be too long for program
- ;; args in `start-process'.
- `(with-temp-message "Initializing asynchronous export process"
- (let ((,copy-fun (org-export--generate-copy-script (current-buffer)))
- (,temp-file (make-temp-file "org-export-process"))
- (,coding buffer-file-coding-system))
- (with-temp-file ,temp-file
- (insert
- ;; Null characters (from variable values) are inserted
- ;; within the file. As a consequence, coding system for
- ;; buffer contents will not be recognized properly. So,
- ;; we make sure it is the same as the one used to display
- ;; the original buffer.
- (format ";; -*- coding: %s; -*-\n%S"
- ,coding
- `(with-temp-buffer
- (when org-export-async-debug '(setq debug-on-error t))
- ;; Ignore `kill-emacs-hook' and code evaluation
- ;; queries from Babel as we need a truly
- ;; non-interactive process.
- (setq kill-emacs-hook nil
- org-babel-confirm-evaluate-answer-no t)
- ;; Initialize export framework.
- (require 'ox)
- ;; Re-create current buffer there.
- (funcall ,,copy-fun)
- (restore-buffer-modified-p nil)
- ;; Sexp to evaluate in the buffer.
- (print (progn ,,@body))))))
- ;; Start external process.
- (let* ((process-connection-type nil)
- (,proc-buffer (generate-new-buffer-name "*Org Export Process*"))
- (,process
- (apply
- #'start-process
- (append
- (list "org-export-process"
- ,proc-buffer
- (expand-file-name invocation-name invocation-directory)
- "--batch")
- (if org-export-async-init-file
- (list "-Q" "-l" org-export-async-init-file)
- (list "-l" user-init-file))
- (list "-l" ,temp-file)))))
- ;; Register running process in stack.
- (org-export-add-to-stack (get-buffer ,proc-buffer) nil ,process)
- ;; Set-up sentinel in order to catch results.
- (let ((handler ,fun))
- (set-process-sentinel
- ,process
- `(lambda (p status)
- (let ((proc-buffer (process-buffer p)))
- (when (eq (process-status p) 'exit)
- (unwind-protect
- (if (zerop (process-exit-status p))
- (unwind-protect
- (let ((results
- (with-current-buffer proc-buffer
- (goto-char (point-max))
- (backward-sexp)
- (read (current-buffer)))))
- (funcall ,handler results))
- (unless org-export-async-debug
- (and (get-buffer proc-buffer)
- (kill-buffer proc-buffer))))
- (org-export-add-to-stack proc-buffer nil p)
- (ding)
- (message "Process `%s' exited abnormally" p))
- (unless org-export-async-debug
- (delete-file ,,temp-file)))))))))))))
+ (declare (indent 1))
+ ;; Write the full sexp evaluating BODY in a copy of the current
+ ;; buffer to a temporary file, as it may be too long for program
+ ;; args in `start-process'.
+ (with-temp-message "Initializing asynchronous export process"
+ (let ((copy-fun (org-export--generate-copy-script (current-buffer)))
+ (temp-file (make-temp-file "org-export-process")))
+ (let ((coding-system-for-write 'utf-8-emacs-unix))
+ (write-region
+ ;; Null characters (from variable values) are inserted
+ ;; within the file. As a consequence, coding system for
+ ;; buffer contents could fail to be recognized properly.
+ (format ";; -*- coding: utf-8-emacs-unix; lexical-binding:t -*-\n%S"
+ `(with-temp-buffer
+ ,(when org-export-async-debug '(setq debug-on-error t))
+ ;; Ignore `kill-emacs-hook' and code evaluation
+ ;; queries from Babel as we need a truly
+ ;; non-interactive process.
+ (setq kill-emacs-hook nil
+ org-babel-confirm-evaluate-answer-no t)
+ ;; Initialize export framework.
+ (require 'ox)
+ ;; Re-create current buffer there.
+ (funcall ',copy-fun)
+ (restore-buffer-modified-p nil)
+ ;; Sexp to evaluate in the buffer.
+ (print ,body)))
+ nil temp-file nil 'silent))
+ ;; Start external process.
+ (let* ((process-connection-type nil)
+ (proc-buffer (generate-new-buffer-name "*Org Export Process*"))
+ (process
+ (apply
+ #'start-process
+ (append
+ (list "org-export-process"
+ proc-buffer
+ (expand-file-name invocation-name invocation-directory)
+ "--batch")
+ (if org-export-async-init-file
+ (list "-Q" "-l" org-export-async-init-file)
+ (list "-l" user-init-file))
+ (list "-l" temp-file)))))
+ ;; Register running process in stack.
+ (org-export-add-to-stack (get-buffer proc-buffer) nil process)
+ ;; Set-up sentinel in order to catch results.
+ (let ((handler fun))
+ (set-process-sentinel
+ process
+ (lambda (p _status)
+ (let ((proc-buffer (process-buffer p)))
+ (when (eq (process-status p) 'exit)
+ (unwind-protect
+ (if (zerop (process-exit-status p))
+ (unwind-protect
+ (let ((results
+ (with-current-buffer proc-buffer
+ (goto-char (point-max))
+ (backward-sexp)
+ (read (current-buffer)))))
+ (funcall handler results))
+ (unless org-export-async-debug
+ (and (get-buffer proc-buffer)
+ (kill-buffer proc-buffer))))
+ (org-export-add-to-stack proc-buffer nil p)
+ (ding)
+ (message "Process `%s' exited abnormally" p))
+ (unless org-export-async-debug
+ (delete-file temp-file))))))))))))
;;;###autoload
(defun org-export-to-buffer
@@ -6325,14 +6329,15 @@ This function returns BUFFER."
(declare (indent 2))
(if async
(org-export-async-start
- `(lambda (output)
- (with-current-buffer (get-buffer-create ,buffer)
- (erase-buffer)
- (setq buffer-file-coding-system ',buffer-file-coding-system)
- (insert output)
- (goto-char (point-min))
- (org-export-add-to-stack (current-buffer) ',backend)
- (ignore-errors (funcall ,post-process))))
+ (let ((cs buffer-file-coding-system))
+ (lambda (output)
+ (with-current-buffer (get-buffer-create buffer)
+ (erase-buffer)
+ (setq buffer-file-coding-system cs)
+ (insert output)
+ (goto-char (point-min))
+ (org-export-add-to-stack (current-buffer) backend)
+ (ignore-errors (funcall post-process)))))
`(org-export-as
',backend ,subtreep ,visible-only ,body-only ',ext-plist))
(let ((output
@@ -6391,8 +6396,8 @@ or FILE."
(encoding (or org-export-coding-system buffer-file-coding-system)))
(if async
(org-export-async-start
- `(lambda (file)
- (org-export-add-to-stack (expand-file-name file) ',backend))
+ (lambda (file)
+ (org-export-add-to-stack (expand-file-name file) backend))
`(let ((output
(org-export-as
',backend ,subtreep ,visible-only ,body-only
@@ -6523,16 +6528,16 @@ within Emacs."
(defvar org-export-stack-mode-map
(let ((km (make-sparse-keymap)))
(set-keymap-parent km tabulated-list-mode-map)
- (define-key km " " 'next-line)
- (define-key km "\C-n" 'next-line)
- (define-key km [down] 'next-line)
- (define-key km "\C-p" 'previous-line)
- (define-key km "\C-?" 'previous-line)
- (define-key km [up] 'previous-line)
- (define-key km "C" 'org-export-stack-clear)
- (define-key km "v" 'org-export-stack-view)
- (define-key km (kbd "RET") 'org-export-stack-view)
- (define-key km "d" 'org-export-stack-remove)
+ (define-key km " " #'next-line)
+ (define-key km "\C-n" #'next-line)
+ (define-key km [down] #'next-line)
+ (define-key km "\C-p" #'previous-line)
+ (define-key km "\C-?" #'previous-line)
+ (define-key km [up] #'previous-line)
+ (define-key km "C" #'org-export-stack-clear)
+ (define-key km "v" #'org-export-stack-view)
+ (define-key km (kbd "RET") #'org-export-stack-view)
+ (define-key km "d" #'org-export-stack-remove)
km)
"Keymap for Org Export Stack.")
@@ -6749,16 +6754,16 @@ back to standard interface."
(cond ((and (numberp key-a) (numberp key-b))
(< key-a key-b))
((numberp key-b) t)))))
- 'car-less-than-car))
+ #'car-less-than-car))
;; Compute a list of allowed keys based on the first key
;; pressed, if any. Some keys
;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always
;; available.
(allowed-keys
(nconc (list 2 22 19 6 1)
- (if (not first-key) (org-uniquify (mapcar 'car entries))
+ (if (not first-key) (org-uniquify (mapcar #'car entries))
(let (sub-menu)
- (dolist (entry entries (sort (mapcar 'car sub-menu) '<))
+ (dolist (entry entries (sort (mapcar #'car sub-menu) #'<))
(when (eq (car entry) first-key)
(setq sub-menu (append (nth 2 entry) sub-menu))))))
(cond ((eq first-key ?P) (list ?f ?p ?x ?a))
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: More use of lexical-binding in ox.el
2021-04-20 3:37 More use of lexical-binding in ox.el Stefan Monnier
@ 2021-04-27 21:12 ` Nicolas Goaziou
2021-04-27 22:51 ` Stefan Monnier
0 siblings, 1 reply; 5+ messages in thread
From: Nicolas Goaziou @ 2021-04-27 21:12 UTC (permalink / raw)
To: Stefan Monnier; +Cc: emacs-orgmode
Hello,
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> Here's another patch to remove some more use of the old dynamically
> scoped dialect of ELisp.
>
> Stefan
>
>
> * lisp/ox.el: Fix various uses of the non-lexical-binding ELisp dialect.
> (org-export--get-global-options, org-export-insert-default-template):
> Use lexical-binding.
> (org-export--generate-copy-script): Return a closure rather than
> list starting with `lambda`.
> (org-export-async-start): Turn it into a function (there seems to be
> no reason this was a macro). Use `write-region` rather than
> `with-temp-file`. Always use `utf-8-emacs-unix` coding system since
> it's more efficient and is guaranteed to handle all chars.
> Use lexical-binding in the temp file as well.
> Actually set `debug-on-error` if `org-export-async-debug` says so.
> (org-export-to-buffer, org-export-to-file): Pass a closure rather than
> list starting with `lambda` to `org-export-async-start`.
Thank you!
It looks great but it introduces a test failure, however.
`org-export-expand-include-keyword' is called from within
`org-export-with-buffer-copy'.
At the very beginning of `org-export-expand-include-keyword', there is
(buffer-file-name (buffer-base-buffer))
Before the patch, it returned the source file name. After the patch it
returns nil.
Actually I'm a bit surprised it used to work, since we're evaluating
this from a new buffer, not an existing one. But hey, it worked!
Do you know what could cause this?
Regards,
--
Nicolas Goaziou
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: More use of lexical-binding in ox.el
2021-04-27 21:12 ` Nicolas Goaziou
@ 2021-04-27 22:51 ` Stefan Monnier
2021-04-28 8:45 ` Nicolas Goaziou
0 siblings, 1 reply; 5+ messages in thread
From: Stefan Monnier @ 2021-04-27 22:51 UTC (permalink / raw)
To: emacs-orgmode
> It looks great but it introduces a test failure, however.
>
> `org-export-expand-include-keyword' is called from within
> `org-export-with-buffer-copy'.
>
> At the very beginning of `org-export-expand-include-keyword', there is
>
> (buffer-file-name (buffer-base-buffer))
>
> Before the patch, it returned the source file name. After the patch it
> returns nil.
>
> Actually I'm a bit surprised it used to work, since we're evaluating
> this from a new buffer, not an existing one. But hey, it worked!
That's because the "buffer copy" also copies the local vars, and indeed
that's where I made a typo.
> Do you know what could cause this?
Yes:
(push (cons var val) varvals))))
varvals)))
should be
(push (cons var val) varvals)))))
varvals))
so that the final `varvals` is outside the `dolist` (it's the thing we
want to return to store it in the outer `varvals` variable).
Sorry 'bout that,
Stefan
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: More use of lexical-binding in ox.el
2021-04-27 22:51 ` Stefan Monnier
@ 2021-04-28 8:45 ` Nicolas Goaziou
2021-04-28 14:15 ` Stefan Monnier
0 siblings, 1 reply; 5+ messages in thread
From: Nicolas Goaziou @ 2021-04-28 8:45 UTC (permalink / raw)
To: Stefan Monnier; +Cc: emacs-orgmode
Hello,
Stefan Monnier <monnier@iro.umontreal.ca> writes:
> That's because the "buffer copy" also copies the local vars,
For some reason, I misread the code and thought `buffer-file-name' and
`default-directory' were explicitly ignored. Go figure.
> Yes:
>
> (push (cons var val) varvals))))
> varvals)))
>
> should be
>
> (push (cons var val) varvals)))))
> varvals))
>
> so that the final `varvals` is outside the `dolist` (it's the thing we
> want to return to store it in the outer `varvals` variable).
Indeed. I couldn't spot this.
I applied your patch.
Thank you!
Regards,
--
Nicolas Goaziou
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: More use of lexical-binding in ox.el
2021-04-28 8:45 ` Nicolas Goaziou
@ 2021-04-28 14:15 ` Stefan Monnier
0 siblings, 0 replies; 5+ messages in thread
From: Stefan Monnier @ 2021-04-28 14:15 UTC (permalink / raw)
To: emacs-orgmode
>> Yes:
>>
>> (push (cons var val) varvals))))
>> varvals)))
>>
>> should be
>>
>> (push (cons var val) varvals)))))
>> varvals))
>>
>> so that the final `varvals` is outside the `dolist` (it's the thing we
>> want to return to store it in the outer `varvals` variable).
>
> Indeed. I couldn't spot this.
Yes, I tried to make it discrete.
Sadly, your test suite wasn't fooled,
Stefan
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-04-28 14:16 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-04-20 3:37 More use of lexical-binding in ox.el Stefan Monnier
2021-04-27 21:12 ` Nicolas Goaziou
2021-04-27 22:51 ` Stefan Monnier
2021-04-28 8:45 ` Nicolas Goaziou
2021-04-28 14:15 ` Stefan Monnier
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).