I have never been able to come up with a method to penetrate the set-process-sentinel hierarchy with let-bound variables defined at the outset of the function -- only buffer-local or global variables can penetrate it. Let-bound variables can reach the first start-process, but that is as far as they can penetrate without being rejected due to being unrecognized -- let-bound variables defined at the outset of the function do not appear to be able to penetrate the section that begins with (lambda (p e) . . .. Can anyone think of a way to do it, including penetrating nested sentinels like in the example below?
(set-process-sentinel
(start-process
"my-process-name-one"
"*OUTPUT-BUFFER*"
"/path/to/executable"
"argument-one"
"argument-two"
"argument-three")
(lambda (p e) (when (= 0 (process-exit-status p))
(set-process-sentinel
(start-process
"my-process-name-two"
nil ;; example of not using an output buffer
"/path/to/executable"
"argument-one"
"argument-two"
"argument-three")
(lambda (p e) (when (= 0 (process-exit-status p))
(set-process-sentinel
(start-process . . . ))))))))
The problem is that Emacs Lisp variable bindings are dynamic by default. That is, when a function is evaluated, bound variables are looked up not in the environment where the function was defined, but in the environment where the function was called.
Emacs 24 or later supports lexical binding (that is, the function sees the variables that were bound around the function definition) natively, but since it alters the semantics of existing code you need to enable it explicitly. Usually this is done by adding a file local variable setting to the first line of the .el file:
;; -*- lexical-binding: t; -*-
Another alternative is to use lexical-let from the cl library. This works in earlier Emacs versions as well. Note that in this way you explicitly specify which variables should have lexical binding, so code such as (lexical-let ((foo foo)) ...) is not uncommon — foo is an existing variable which needs to be "carried over" into the function.
The following is an example using dynamic bindings:
(defun example-dynamic-fn ()
"Doc-string"
(interactive)
(let ((test-variable "Hello-world!"))
(set-process-sentinel
(start-process "process-one" "*one*" "echo" test-variable)
`(lambda (p e) (when (= 0 (process-exit-status p))
(set-process-sentinel
(start-process "process-two" "*two*" "echo" ,test-variable)
'(lambda (p e) (when (= 0 (process-exit-status p))
(start-process "process-three" "*three*" "echo" ,test-variable)
(set-process-sentinel
(start-process "process-four" "*four*" "echo" ,test-variable)
'(lambda (p e) (when (= 0 (process-exit-status p))
(set-process-sentinel
(start-process "process-five" "*five*" "echo" ,test-variable)
'(lambda (p e) (when (= 0 (process-exit-status p))
(message "test-variable: %s" ,test-variable)))))))))))))))
OK, I think I've got this now. The link above provides a good example; here's another in case anyone else has this difficulty:
;;; ensure VAR1 has no binding
(makunbound 'VAR1)
;;;
(defun f1 (&optional VAR1)
(interactive)
(unless VAR1
(set 'VAR1 "variable1"))
(pop-to-buffer "*test*")
; (lexical-let ( (VAR1 VAR1) ) ;;;
(set-process-sentinel
(start-process-shell-command "test"
"*test*"
(concat "echo " VAR1))
(lambda (process event)
(condition-case err
(when (string-match-p "finished" event)
(f2 VAR1))
(error
(princ
(format "Sentinel error: %s" err))))))
; ) ;;;
)
;;;
(defun f2 (&optional VAR2)
(interactive)
(unless VAR2
(set 'VAR2 "VARIABLE2"))
(print VAR2))
We load everything (with the lines in (f1) commented out) and run (f1). The value of VAR1 is passed to (f2) before the error occurs. The error (void-variable VAR1) appears to come from the scoping environment of (set-process sentinel PROCESS SENTINEL); VAR1 is not defined there even though it remains in scope for the SENTINEL ((lambda)) function.
Also using (set ) as above is not best practice when a variable is only meant to have scope local to the function.
If we uncomment the lines marked with ; then everything works as expected. Happily, we can pass the value through to another function, which prevents a long series of (set-process sentinel )s building up. It also allows us to generate processes with additional sub-processes, if required.
One of my mistakes was naming the SENTINEL as a discrete function, rather than keeping it inside the (lexical-let ) function. While the lexical-binding: t; approach is attractive, it will tend to break working code which relies on the standard (let ).
Related
I have two functions that return an anonymous function, and almost all the content in the functions are the same except for a few lines.
(defun foo ()
(interactive)
(lambda (arg)
(when (not (string-empty-p arg))
(message "foo")
(message arg))))
(defun bar ()
(interactive)
(lambda (arg)
(when (not (string-empty-p arg))
(message "bar")
(message arg))))
Therefore, I created a function that received lists as argument and returned an anonymous function as follows:
(defun make-anonymous-func (&rest body)
(interactive)
(lambda (arg)
`(when (not (string-empty-p arg))
,#body
(message arg))))
However, when I executed the function returned by make-anonymous-func, the list in lambda was recognized as list literal and wasn't properly evaluated.
(funcall (make-anonymous-func '(message "foo")) "bar")
; ===> (when (not (string-empty-p arg)) (message "foo") (message arg))
Are there some good ways to make it work?
Thanks.
A form like
`(when (not (string-empty-p arg))
,#body
(message arg))
is equivalent to something like this:
(append '(when (not (string-empty-p arg)))
body
((message arg))))
So your function can be rewritten like this:
(defun make-anonymous-func (&rest body)
(lambda (arg)
(append '(when (not (string-empty-p arg)))
body
`((message arg)))))
And I think it's now very clear why this can't work. More generally, backquote forms provide a concise way of describing ways of constructing s-expressions by filling in templates, which is useful in macros, since s-expressions represent Lisp source code and macros are essentially functions whose values are source code, ad well as in other places where you want to construct s-expressions from templates.
(One horrible approach to do what you want is eval which evaluates source code. There are many reasons why this is a very bad answer however, so I won't go into it here.)
The general way to do what you're after is to rely on lexical scope. Conveniently elisp now has lexical scope, so you actually can do this. The following two examples assume that lexical-binding is true.
First of all if you merely want to capture the value of some variable bindings then you can do this:
(defun make-anonymous-func (msg)
(lambda (arg)
(when (not (string-empty-p arg))
(message msg)
(message arg))))
The second approach is if you want the function to run some general code: the way to do this is to pass it a function to call:
(defun make-anonymous-func (f)
(lambda (arg)
(when (not (string-empty-p arg))
(funcall f arg)
(message arg))))
And now
(funcall (make-anonymous-func
(lambda (m)
(message "foo")
(sit-for 1)))
"x")
Will cause two messages to appear, but this time there will be a pause between them so you'll see the first.
A final possibility, if what you want to do is create a bunch of similar functions statically – at compile time or definition time rather than dynamically at runtime – is to use a macro.
(defmacro define-messaging-function (name args &rest forms)
;; elisp has no destructuring, so check
(unless (and (listp args) (= (length args) 1)
(symbolp (car args)))
(error "bad arguments"))
(let ((arg (car args)))
`(defun ,name (,arg)
(when (not (string-empty-p ,arg))
,#forms
(message ,arg)))))
And now
(define-messaging-function foo (x)
(message "foo"))
Defines foo as a function which, when called, will do what the function returned by your original foo did.
Under Linux, eshell-autojump will do case sensitive matching which I just find a nuisance. I've tried to circumvent this by advising eshell/j with a eshell-under-windows-p that always returns t but to my chagrin eshell-under-windows-p invoked in eshell/j is unaffected by cl-letf. I've modified my eshell/j a bit to give me some debug info:
;; Modified eshell/j inside eshell-autojump.el to this
(defun eshell/j (&rest args) ; all but first ignored
"Jump to a directory you often cd to.
This compares the argument with the list of directories you usually jump to.
Without an argument, list the ten most common directories.
With a positive integer argument, list the n most common directories.
Otherwise, call `eshell/cd' with the result."
(setq args (eshell-flatten-list args))
(let ((path (car args))
(candidates (eshell-autojump-candidates))
(case-fold-search (eshell-under-windows-p))
result)
(when (not path)
(setq path 10))
(message "case-fold-search = %S" case-fold-search)
(message "eshell-under-windows-p returns %s from inside eshell/j" (eshell-under-windows-p))
(if (and (integerp path) (> path 0))
(progn
(let ((n (nthcdr (1- path) candidates)))
(when n
(setcdr n nil)))
(eshell-lisp-command (mapconcat 'identity candidates "\n")))
(while (and candidates (not result))
(if (string-match path (car candidates))
(setq result (car candidates))
(setq candidates (cdr candidates))))
(eshell/cd result))))
My init.el adds the advice to attempt to make eshell/j caseless by trying to trick it to think we are on Windows:
;; Added to init.el
(require 'eshell-autojump)
(advice-add 'eshell/j :around
(lambda (orig-fun &rest xs)
(cl-letf (((symbol-function 'eshell-under-windows-p) (lambda () t)))
(progn (message "eshell-under-windows-p returns %s from lambda" (eshell-under-windows-p)) (apply orig-fun xs)))))
But all I get in Messages buffer when I try to jump in eshell is:
;; I get in *Messages*
eshell-under-windows-p returns t from lambda
case-fold-search = nil
eshell-under-windows-p returns nil from inside eshell/j
My rookie knowledge of elisp is not enough to wrestle with probable scoping issues here. Can anyone decode why eshell-under-window-p is unaffected when called from eshell/j here?
I've found the answer. cl-letf does not work for byte compiled functions. As eshell-autojump is a package it gets byte compiled upon installation and cl-letf cannot be used to modify it's internal behavior. I had to resort to redefining the eshell/j which is a suboptimal solution.
Is there a test to determine what type of hook is running -- e.g., window-configuration-change-hook?
For example, I have a function that contains conditions used determine whether it should be run -- it is used in conjunction with post-command-hook. I would like to use the same function for the window-configuration-change-hook, without triggering the first set of conditions:
(when
(or
(and
(not window-configuration-change-hook) ;; illustrative example only
(memq this-command this-last-command-inclusions)
(not multiple-cursors-mode))
window-configuration-change-hook) ;; illustrative example only
. . .
AFAIK there is no standardized method to determine which hook is running.
If you want to use advice or something like that to store this information you have to be aware that hooks can run within hooks. See the following example.
That means you need a stack-like structure to store that information.
EDIT: The example includes now a hook-stack storing the currently running hooks.
Note, I do not recommend to use this method since it is quite critical and does not work in all cases. Better, advice the mode/function or whatever what you want to identify.
(defvar hook-stack nil)
(defadvice run-hooks (around register-hooks activate)
"Store current hook into `hook-stack'."
(let ((hooks (ad-get-args 0)))
(loop for h in hooks do
(unwind-protect
(progn
(push h hook-stack)
(ad-set-args 0 (list h))
ad-do-it))
(pop hook-stack)
)))
(ad-remove-advice 'run-hooks 'around 'register-hooks)
(setq hooks1 nil)
(setq hooks2 nil)
(add-hook 'hooks1 (lambda () (message "Running hooks1, hook-stack: %S" hook-stack)))
(add-hook 'hooks2 (lambda () (message "Running hooks2") (run-hooks 'hooks1)
(message "Finishing hooks2, hook-stack: %S" hook-stack)))
(run-hooks 'hooks2)
Note: This does not work if run-hooks is called from C instaead of lisp. Furthermore, there are other functions like run-hook-with-args-until-success.
I'm working on a function to break out of edebug while keeping
the arguments of current function bound.
(defun stop-edebug ()
"Stop edebugging"
(interactive)
(if (not edebug-mode)
(error "edebug isn't running!")
(save-excursion
(beginning-of-defun)
(if (looking-at "(defun\\s-+\\_<[^ ]+\\_>\\s-+(")
(progn
(goto-char (match-end 0))
(backward-char 1)
(forward-sexp 1)
(let ((sexps (mapcar
(lambda(x!) (cons x! (symbol-value x!)))
(preceding-sexp))))
(edebug-mode -1)
(mapc (lambda(x) (set (car x) (cdr x))) sexps)))))))
This way i can eval the function body at my own pace after edebug has bound
the function arguments for me.
The problem is that (edebug-mode -1) isn't really the way to exit
edebug. It leads edebug marker lingering in the buffer and maybe other
side-effects that I'm not aware of.
Normally, exiting is done with q that's bound to top-level.
But I don't how to call anything after top-level since
it's a jump straight to the main command loop.
So I'm asking either for a way to call something after top-level
or for a better way to exit edebug than (edebug-mode -1).
To run code after calling top-level you can schedule that code for later execution in a timer. E.g.
...
(run-with-timer 0 nil
(lambda ()
(do the thing (here))))
(top-level))
In emacs 24, set-temporary-overlay-map makes active a keymap which becomes inactivated as soon as the user presses a key which is not defined in that keymap.
I need to inactivate the overlay keymap manually, but no function is provided to do this in particular. I've peeked into the source code:
(defun set-temporary-overlay-map (map &optional keep-pred)
"Set MAP as a temporary keymap taking precedence over most other keymaps.
Note that this does NOT take precedence over the \"overriding\" maps
`overriding-terminal-local-map' and `overriding-local-map' (or the
`keymap' text property). Unlike those maps, if no match for a key is
found in MAP, the normal key lookup sequence then continues.
Normally, MAP is used only once. If the optional argument
KEEP-PRED is t, MAP stays active if a key from MAP is used.
KEEP-PRED can also be a function of no arguments: if it returns
non-nil then MAP stays active."
(let* ((clearfunsym (make-symbol "clear-temporary-overlay-map"))
(overlaysym (make-symbol "t"))
(alist (list (cons overlaysym map)))
(clearfun
;; FIXME: Use lexical-binding.
`(lambda ()
(unless ,(cond ((null keep-pred) nil)
((eq t keep-pred)
`(eq this-command
(lookup-key ',map
(this-command-keys-vector))))
(t `(funcall ',keep-pred)))
(set ',overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook ',clearfunsym)
(setq emulation-mode-map-alists
(delq ',alist emulation-mode-map-alists))))))
(set overlaysym overlaysym)
(fset clearfunsym clearfun)
(add-hook 'pre-command-hook clearfunsym)
;; FIXME: That's the keymaps with highest precedence, except for
;; the `keymap' text-property ;-(
(push alist emulation-mode-map-alists)))
I gather that the mechanism to inactivate the current overlay keymap is as follows:
A function clearfun is defined to run before every command, checking if the previous command invoked was in the map.
If it was not in the map, the following code is executed:
(Why doesn't this format correctly? Ok, now it does)
(set ',overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook ',clearfunsym)
(setq emulation-mode-map-alists
(delq ',alist emulation-mode-map-alists))
Thus, what I really want is to execute the code above with the appropriate variables. But this code is part of a closure, and I'm having trouble determining values like overlaysym, clearfunsym, alist inside the closure. I tried looking for clearfunsym by eval-ing pre-command-hook, but strangely nothing is there (except for another unrelated hook).
I tried re-evaluating the function defintion and edebugging it, and I notcied after the (add-hook 'pre-command-hook clearfunsym), pre-command-hook is still nil, which puzzles me. I will continue digging deeper into the source code, and maybe I will just rewrite my own version of this function to additionally produce a force-clear function that I can call later, but maybe someone can see a cleaner solution.
You wrote: "I'm having trouble determining values like overlaysym"
But, overlaysym is evaluated. It has the value (make-symbol "t").
It is a symbol with name t. This makes it hard to access it but not impossible.
Evaluation of the following lines gives the out-commented results:
(setq mysym (make-symbol "t"))
;; t
(set mysym 'test)
;; test
(symbol-value mysym)
;; test
The same applies to clearfunsym which evaluates to clear-temporary-overlay-map.
One more comment: When you debug set-temporary-overlay-map you are hitting keys. Might it be that these keystrokes call clear-temporary-overlay-map and clear pre-command-hook?
Try that:
(defadvice set-temporary-overlay-map (after test activate)
(setq test-pre-command-hook pre-command-hook))
Then enter text-scale-mode (C-+) and look at test-pre-command-hook. For an istance evaluating test-pre-command-hook on my computer gave me the following list:
(clear-temporary-overlay-map tooltip-hide).
Let us do the same with emulation-mode-map-alists. Then we get:
(((t keymap (67108912 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
...
(fn)" nil]) (45 . #[0 "\301\302\300!!\207" [1 text-scale-adjust abs] 3 "
(fn)" nil]))))
Especially, note the t at the beginning. That means you find the overlay map by searching for the list with symbol t at the beginning.
Something like the following code fragment should be sufficient to delete the overlay map:
(when (assoc-string "t" (car emulation-mode-map-alists))
(setq emulation-mode-map-alists (cdr emulation-mode-map-alists)))
The when is just a protection. (Maybe, something else has killed the map before?) The temporary map should always be at the front (because of the push in set-temporary-overlay-map). Does anything have a chance to put another keymap in front of it? Maybe, something time-controlled? Then you would need to search emulation-mode-map-alists for the alist with the (make-symbol "t") keymap.
The original set-temporary-overlay-map is very confusing, unreadable, and relies on a lot of unnecessary dirty hacks and tricks. Using lexical binding, I have rewritten the function in a way that is more clear and modular.
The revised function uses lexical-binding to replace the eager-lazy evaluation hacks (in this case they are completely unnecessary).
The original function unnecessarily creates two symbols for each function, (clearfunsym and clear-fun are basically the same thing, the later being used only as the function cell of the former)
The revised function provides an force-overlay-clear, which is called by the clear-temporary-overlay-map pre-command-hook when the conditions are met (ie, that the last key was not in the overlay map). It can also be called by the user if he wants to manually clear this map. The function voids force-overlay-clear's own function cell, so it will err if called twice.
Code to test whether clear is applicable simplified.
I was not able to do away with the extremely weird (overlaysym (make-symbol "t")), fearing that some other code might rely on this t symbol. Thus, the revised version is almost certainly equivalent to the original version. I have tested this, and it works nicely.
(defun set-temporary-overlay-map (map &optional keep-pred)
(lexical-let* (
(map map)
(keep-pred keep-pred)
(clear-temporary-overlay-map nil)
(force-overlay-clear nil)
(overlaysym (make-symbol "t"))
(alist (list (cons overlaysym map))))
(fset 'force-overlay-clear (lambda ()
(message "clearing overlay")
;this is a copy of the original code to clear
(set overlaysym nil) ;Just in case.
(remove-hook 'pre-command-hook 'clear-temporary-overlay-map)
(setq emulation-mode-map-alists
(delq alist emulation-mode-map-alists))
;void the function cell of 'force-overlay-clear', an attempt to clear overlay twice will err
(fset 'force-overlay-clear nil)
))
(fset 'clear-temporary-overlay-map (lambda ()
(unless (cond
((null keep-pred) nil)
(keep-pred
(lookup-key map (this-command-keys-vector)))
(t (funcall keep-pred)))
(force-overlay-clear)
)))
(set overlaysym overlaysym)
(add-hook 'pre-command-hook 'clear-temporary-overlay-map)
;; FIXME: That's the keymaps with highest precedence, except for
;; the `keymap' text-property ;-
(push alist emulation-mode-map-alists))
)
You can do the following:
(defun my-exit-command ()
(do-what-the-q-key-should-do))
....(set-temporary-overlay-map
my-overlay-map
(lambda ()
(and (eq this-command (lookup-key my-overlay-map
(this-single-command-keys)))
(not (eq this-command 'my-exit-command)))))
....