I would like to bind TAB to my company-mode back-end only for the duration of a command's execution. I am trying to achieve this by using dynamic scoping as follows (the irrelevant part of the function is not shown):

(defun my-function()
  (let ((company-mode-map (copy-sequence company-mode-map)))
    (define-key company-mode-map (kbd "TAB") 'my-company-backend)

The new binding does not work (though it did work when I performed it outside of my-function). Why does this use of dynamic scoping not produce the result that I expected and how do I fix it?


The comment you wrote on of the other answers was very useful in figuring out what you want to do; I hope I understood correctly. I believe you want to write a command that prompts for a string from the minibuffer with TAB temporarily bound to `my-company-backend.

Assuming the string prompted for is the only argument your command needs, try something like this:

(defun my-function (my-arg)
   (let ((minibuffer-local-map (copy-sequence minibuffer-local-map)))
     (define-key minibuffer-local-map (kbd "TAB") 'my-company-backend)
     (list (read-string "Prompt: " "default string"))))
  MORE-CODE-HERE) ; here my-arg will hold the string the user typed

Inside the backend function? Why? The function will return before the user's next input is processed.

You can change company-active-map, though.

  • I am not sure I understand your question. As for the suggestion, I will look into using active maps, but would still appreciate if you provide a code snippet. – AlwaysLearning Aug 6 '17 at 21:07
  • company-active-map is the name of a global variable. – Dmitry Aug 6 '17 at 21:18
  • But then we are back to the problem that I started with! Namely, I want to make sure that the active map gets restored once my function is done. That is the reason I used let in the first place... – AlwaysLearning Aug 6 '17 at 21:22
  • When is your function done? – Dmitry Aug 7 '17 at 0:27
  • The function is a command. It is done either when it exits with a value, or an error happens, or the user stops it by pressing C-g. I want to use dynamic scoping to be safe in any case. That is, I want the keymap with TAB bound to my back-end to exist only locally inside let and the functions called by my command. I have edited the question to make all this clearer. – AlwaysLearning Aug 7 '17 at 8:13

You can try

(cl-letf (((current-local-map) (make-sparse-keymap)))

But, like mentioned in the comment, I doubt the utility of this.

  • May be I am missing something. I have a command, which takes input from mini-buffer. It puts the mini-buffer in company-mode to have completion from a list of keywords. For that input (and only for that input) I want the TAB key to be bound to my company mode back-end. If the user opens some other buffer in company mode, there should not be any trace of my binding, i.e. TAB should be unbound. So, when you doubt the utility of this, I am thinking that I must be missing something conceptual about Emacs. I will very much appreciate if you correct my mistake. – AlwaysLearning Aug 7 '17 at 18:03
  • You'll need to change the local map of the minibuffer's buffer. Usually that's done in minibuffer-setup-hook. See icomplete-minibuffer-setup as one example of such hook (it calls use-local-map). – Dmitry Aug 7 '17 at 20:50
  • But that will affect any command that uses mini-buffer. If I did not care about that, I could evaluate define-key outside of my command (which is what I used to do and it worked). I am looking for a clean solution, whereby the change of key binding is totally encapsulated inside my command and does not affect any other code. – AlwaysLearning Aug 7 '17 at 21:23
  • You will bind minibuffer-setup-hook temporarily, only inside your function. – Dmitry Aug 8 '17 at 11:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.