2

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()
  (interactive)
  (let ((company-mode-map (copy-sequence company-mode-map)))
    (define-key company-mode-map (kbd "TAB") 'my-company-backend)
    MORE-CODE-HERE))

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?

AlwaysLearning
  • 749
  • 4
  • 14

3 Answers3

1

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)
  (interactive
   (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
Omar
  • 4,732
  • 1
  • 17
  • 32
  • This works! However, I think that I have discovered a bug in company mode: https://github.com/company-mode/company-mode/issues/690 – AlwaysLearning Aug 13 '17 at 16:39
0

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

You can change company-active-map, though.

Dmitry
  • 3,508
  • 15
  • 20
  • 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 06 '17 at 21:07
  • `company-active-map` is the name of a global variable. – Dmitry Aug 06 '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 06 '17 at 21:22
  • When is your function *done*? – Dmitry Aug 07 '17 at 00: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 07 '17 at 08:13
  • But keymap only takes effect between commands, doesn't it? Aside from the scenario where your code takes over the input loop for a while, but then I'm not sure why you'd want to change the current keymap. – Dmitry Aug 07 '17 at 13:58
0

You can try

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

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

Dmitry
  • 3,508
  • 15
  • 20
  • 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 07 '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 07 '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 07 '17 at 21:23
  • You will bind `minibuffer-setup-hook` temporarily, only inside your function. – Dmitry Aug 08 '17 at 11:04