Emacs -- test to determine what type of hook is running - emacs

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.

Related

How do I add auto-completion to my custom comint mode in Emacs?

I'm writing a comint mode for a legacy command-line tool.
I'd like to add basic auto-completion to it.
Suppose I have the following list of keywords used by the tool:
(defconst my-keywords '("export" "extract" "display"))
How would I add auto-completion to my mode, based on this list?
What I found so far:
I know there are examples of this in shell.el or in comint.el, but I did not manage to understand the code well enough to answer to this basic question. I did understand though that I could build a regexp list out of my-keywords, like this:
(regexp-opt my-keywords)
;; output:
"\\(?:display\\|ex\\(?:\\(?:por\\|trac\\)t\\)\\)"
Other than that, I gathered that I could use pcomplete, or company, or both – I'm fine with any solution actually, but how do I do it?
Comint also calls customizable comint-dynamic-complete-functions from its comint-completion-at-point function. Derived modes will often add functions to this hook (see shell-dynamic-complete-functions), eg.
(defconst my-keywords '("export" "extract" "display"))
(defun my-comint-dynamic-completion-function ()
(when-let* ((bds (bounds-of-thing-at-point 'symbol))
(beg (car bds))
(end (cdr bds)))
(when (> end beg)
(list beg end my-keywords :annotation-function (lambda (_) "my-keywords")))))
(define-derived-mode my-comint-mode comint-mode "my mode"
(add-hook 'comint-dynamic-complete-functions
#'my-comint-dynamic-completion-function nil 'local)
(make-local-variable 'company-backends)
(cl-pushnew 'company-capf company-backends))
By adding company-capf to your company-backends you will additionally get company support from your completion at point function (see elisp-completion-at-point for example of additional company-specific symbols for showing help/location/etc. of completion candidates).
Thanks to ergoemacs I found a first solution :
Define a completion function which returns a list of the form (start end my-keywords . nil), with start and end delimiting the entity to complete at point. I added a tweak to take into account the program prompt, for the first keyword.
Add this function to completion-at-point-functions, in the definition of the mode;
Add a tab shortcut to completion-at-point, in the mode-map.
(defconst my-keywords '("export" "extract" "display"))
;; 1 - custom completion function
(defun my-completion-at-point ()
"This is the function to be used for the hook `completion-at-point-functions'."
(interactive)
(let* (
(bds (bounds-of-thing-at-point 'symbol))
(start (max (car bds) (comint-line-beginning-position)))
(end (cdr bds)))
(list start end xyz-keywords . nil )))
;; 2 - adding it to my-completion-at-point
(define-derived-mode my-comint-mode comint-mode "My comint mode"
;; your code....
(add-hook 'completion-at-point-functions 'my-completion-at-point nil 'local))
;; 3 - add a tab shortcut in the map of the mode
(defvar my-mode-map
(let ((map (nconc (make-sparse-keymap) comint-mode-map)))
;; your code...
(define-key map "\t" 'completion-at-point)
map))

use .dir-locals.el to pick major mode [duplicate]

I have defined a .dir-locals.el file with the following content:
((python-mode . ((cr/virtualenv-name . "saas"))))
In my .emacs I have the following function to retrieve this value and provide a virtualenv path:
(defun cr/virtualenv ()
(cond (cr/virtualenv-name (format "%s/%s" virtualenv-base cr/virtualenv-name))
((getenv "EMACS_VIRTUAL_ENV") (getenv "EMACS_VIRTUAL_ENV"))
(t "~/.emacs.d/python")))
Finally, in my python-mode-hook list, I have this hook function:
(add-hook 'python-mode-hook 'cr/python-mode-shell-setup)
(defun cr/python-mode-shell-setup ()
(message "virtualenv-name is %s" cr/virtualenv-name)
(let ((python-base (cr/virtualenv)))
(cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython")))
(setq python-python-command (concat python-base "/bin/ipython"))
(setq py-python-command (concat python-base "/bin/ipython"))
(setq py-python-command-args '( "-colors" "NoColor")))
(t
(setq python-python-command (concat python-base "/bin/python"))
(setq py-python-command (concat python-base "/bin/python"))
(setq py-python-command-args nil)))))
When I open a new python file, the message logged by cr/python-mode-shell-setup indicates that cr/virtualenv-name is nil. However, when I C-h v the name, I get "saas" instead.
Obviously there's a load order issue here; is there a way to have my mode hook statements respond to directory-local variables?
This happens because normal-mode calls (set-auto-mode) and (hack-local-variables) in that order.
However hack-local-variables-hook is run after the local variables have been processed, which enables some solutions:
The first is to make Emacs run a new "local variables hook" for each major mode:
(add-hook 'hack-local-variables-hook 'run-local-vars-mode-hook)
(defun run-local-vars-mode-hook ()
"Run a hook for the major-mode after the local variables have been processed."
(run-hooks (intern (concat (symbol-name major-mode) "-local-vars-hook"))))
(add-hook 'python-mode-local-vars-hook 'cr/python-mode-shell-setup)
(Your original function can be used unmodified, with that approach.)
A second option is to utilise the optional LOCAL argument to add-hook that makes the specified function buffer-local. With this approach you could write your hook as follows:
(add-hook 'python-mode-hook 'cr/python-mode-shell-setup)
(defun cr/python-mode-shell-setup ()
(add-hook 'hack-local-variables-hook
(lambda () (message "virtualenv-name is %s" cr/virtualenv-name)
(let ((python-base (cr/virtualenv)))
(cond ((and (fboundp 'ipython-shell-hook) (file-executable-p (concat python-base "/bin/ipython")))
(setq python-python-command (concat python-base "/bin/ipython"))
(setq py-python-command (concat python-base "/bin/ipython"))
(setq py-python-command-args '( "-colors" "NoColor")))
(t
(setq python-python-command (concat python-base "/bin/python"))
(setq py-python-command (concat python-base "/bin/python"))
(setq py-python-command-args nil)))))
nil t)) ; buffer-local hack-local-variables-hook
i.e. python-mode-hook runs first and registers the anonymous function with hack-local-variables-hook for the current buffer only; and that function is then called after the local variables have been processed.
Lindydancer's comment prompts a third approach. It's not nearly as clean as the other two, but proved interesting regardless. I didn't like the idea of causing (hack-local-variables) to be called twice, but I see that if you set the local-enable-local-variables buffer-locally, it prevents (hack-local-variables) from doing anything, so you could do this:
(defun cr/python-mode-shell-setup ()
(report-errors "File local-variables error: %s"
(hack-local-variables)))
(set (make-local-variable 'local-enable-local-variables) nil)
(let ((python-base (cr/virtualenv)))
...))
Obviously that modifies the normal sequence of execution a little, so side effects may be possible. I was worried that if the same major mode is set by a local variable comment in the file, this might cause infinite recursion, but that doesn't actually appear to be a problem.
Local variable header comments (e.g. -*- mode: foo -*-) are handled by (set-auto-mode), so those are fine; but a mode: foo Local Variables: comment seems like it would be an issue as it is handled by (hack-local-variables), and so if the mode is set that way I thought it would cause recursion.
In practice I was able to trigger the problem by using a simple function as a 'mode' which did nothing more than try to run its hooks; however testing with a 'proper' mode did not exhibit the problem, so it's probably safe in reality. I didn't look into this further (as the other two solutions are much cleaner than this), but I would guess the delayed mode hooks mechanism probably explains it?

Manually exit a temporary overlay map

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)))))
....

How can I set environment variables to a buffer-local scope in emacs

In Emacs, I want to vary the values of my environment variables in different buffers.
My emacs environment depends on environment variables (flymake, compile etc), however I want to be able to be able to have multiple projects open at once in one emacs session but these projects might have conflicting environments.
For example something like different INCLUDE_PATH environment variables for flymake.
You can do this by making process-environment buffer-local:
(defun setup-some-mode-env ()
(make-local-variable 'process-environment)
;; inspect buffer-file-name and add stuff to process-environment as necessary
...)
(add-hook 'some-major-mode 'setup-some-mode-env)
A more elaborate example is this code that imports the Guile environment setup created by an external script. The script is designed to be "sourced" in the shell, but here its result gets imported into a single Emacs buffer:
(defun my-guile-setup ()
(make-local-variable 'process-environment)
(with-temp-buffer
(call-process "bash" nil t nil "-c"
"source ~/work/guileenv; env | egrep 'GUILE|LD_LIBRARY_PATH'")
(goto-char (point-min))
(while (not (eobp))
(setq process-environment
(cons (buffer-substring (point) (line-end-position))
process-environment))
(forward-line 1))))
(add-hook 'guile-hook 'my-guile-setup)
I put the following in .dir-locals.el at the root of the tree where I want to define some environment vars:
;; variables local to this directory and its children
((nil . ((eval . (setenv "SOME_VARIABLE" "TRUE")))))
This will warn the first time you open a file in that directory tree. After you accept, the given environment var will be defined for each buffer you open there.
One solution would be to temporary change the environment when you spawn an external command. The command will inherit the current environment. Remember that Emacs is a single-treaded application, so we don't have to worry about race conditions etc.
You can pick one of two ways of doing this:
1) Write you own functions like my-compile that changes the environment temporarily and calls the normal compile command.
2) Modify the low-level process functions and ensure that they modify the environment accordingly. Typically, you can do this with defadvice.
It may be possible to use dynamic binding for those variables.
Dynamic binding and Dynamic scoping are a bit hard to explain, for explanations see http://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding and
http://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping.
Here is an example where I create a local process-environment without necessarily making it buffer-local. The advantage is that the settings only affect the running process and they disappear once the process ends. In this example, I set the timzezone environmental variable and call the function with (funcall my-start-process ....) and everything else is just like start-process in terms of arguments and so forth.
(let* ((my-start-process
(lambda (name buffer program &rest program-args)
(unless (fboundp 'make-process)
(error "Emacs was compiled without subprocess support"))
(let* (
(temp (mapcar 'concat process-environment))
(newenv
(cond
((equal (car (cdr (current-time-zone))) "PDT")
(setenv-internal temp "TZ" "UTC+7" t))
((equal (car (cdr (current-time-zone))) "PST")
(setenv-internal temp "TZ" "UTC+8" t))))
(process-environment (or newenv temp)))
(apply #'make-process
(append (list :name name :buffer buffer)
(when program
(list :command (cons program program-args))))))))
(proc (funcall my-start-process ...))))

AUCTeX: Run Compile Command n-times

I'd like to have a function that asks for a number n and executes the default compile command n-times afterwards. That is to say unlike C-c C-c (i.e. TeX-command-master) I don't want to be asked which command to run, it should select the default compile command based on the AUCTeX settings. Naturally if any error occurs the execution should stop.
I know about TeX-texify, however, this doesn't statisfy my needs because sometimes I just want emacs to run pdflatex five times indepent of what the AUCTeX parser thinks is adequate.
Any help is much appreciated!
Edit: I have looked into this a little further and using code from the above reference I have started writing a function that does this. However, it has one major flaw. Let me first give you the code:
(defcustom TeX-MultiTeX-Command "LaTeX" "Default MultiTeX command" :type 'string :group 'TeX-command)
(defun TeX-MultiTeX (n)
"Run TeX-command n-times"
(interactive "nRun TeX/LaTeX how many times: ")
(while (> n 0)
(TeX-command TeX-MultiTeX-Command 'TeX-master-file)
(setq n (- n 1))))
As you can see, I have implemented a config variable for selecting the correct compilation command. Now let me present the problem:
The compilation of the LaTeX document takes some time, however, my function instantly calls the second (and following) executions of the compile command. Maybe someone can provide help in finding a solution that checks whether compilation has finished successfully prior to executing (TeX-command TeX-MultiTeX-Command 'TeX-master-file), then executes said function or prints some error message if compilation finished with an error.
With the help of the code of the TeX-texify function I have developed a function that does what I want, the code is given below.
I'd like to thank user4815162342; although this solution is not based on his suggestion, I think his solution might be of use for a different problem. Also I'd like to thank TN, the author of TeX-texify, I shamelessly took and adapted his code for my problem. ;)
(defcustom TeX-MultiTeX-Command "LaTeX"
"Default MultiTeX command"
:type 'string :group 'TeX-command)
(defun TeX-MultiTeX-sentinel (&optional proc sentinel)
"Non-interactive! Call the standard-sentinel of the current LaTeX-process.
If there is still something left do do start the next latex-command."
(set-buffer (process-buffer proc))
(funcall TeX-MultiTeX-sentinel proc sentinel)
(let ((case-fold-search nil))
(when (string-match "\\(finished\\|exited\\)" sentinel)
(set-buffer TeX-command-buffer)
(unless (plist-get TeX-error-report-switches (intern (TeX-master-file)))
(TeX-MultiTeX TeX-MultiTeX-num-left)))))
(defun TeX-MultiTeX (n)
"Run TeX-command n-times"
(interactive "nRun TeX/LaTeX how many times: ")
(when (or (called-interactively-p 'any)
(null (boundp 'TeX-MultiTeX-num-left)))
(setq TeX-MultiTeX-num-left n))
(if (>= TeX-MultiTeX-num-left 1)
(progn
(TeX-command TeX-MultiTeX-Command 'TeX-master-file)
(setq TeX-MultiTeX-num-left (- TeX-MultiTeX-num-left 1))
(setq proc (get-buffer-process (current-buffer)))
(setq TeX-MultiTeX-sentinel (process-sentinel proc))
(set-process-sentinel proc 'TeX-MultiTeX-sentinel))))
It seems that you need a synchronous way to run TeX-command. I haven't word with TeX-command, but if it uses the compilation API, it can be made to wait for the compilation to finish, although it's not exactly obvious how to do that. Here is an example that uses compilation-finish-functions to achieve the desired effect:
(require 'cl) ; for lexical-let
(defun compile-and-wait (compilefun)
(interactive)
(lexical-let ((done nil) finish-callback)
(setq finish-callback
;; when the compilation is done, remove the callback from
;; compilation-finish-functions and interrupt the wait
(lambda (buf msg)
(setq compilation-finish-functions
(delq finish-callback compilation-finish-functions))
(setq done t)))
(push finish-callback compilation-finish-functions)
(funcall compilefun)
(while (not done)
(sleep-for .1))))
EDIT
AUC TeX is not using compilation mode to spawn TeX, so the above cannot work. Since it's still useful for other compilation buffers, I'm leaving it in the answer. Another way to implement TeX-MultiTeX is by binding TeX-process-asynchronous to nil, which should ensure that AUC TeX waits for the command to finish.