1

In term-mode I've configured the keybindings for term-line-mode and term-char-mode as follows:

(setq term-bind-key-alist
      (list (cons "C-c C-j" '(lambda () (interactive)
                               (term-line-mode)
                               (evil-normal-state)))
            (cons "C-c C-k" '(lambda () (interactive)
                               (term-char-mode)
                               (evil-emacs-state)
                               (end-of-buffer)))

This gives the expected behavior when I type "C-c C-j" but only term-char-mode seems to be called when I type "C-c C-k". All of them are called if I type "C-c C-k" for a 2nd time. How can I get them to work on the first key press?


Edit 1

Here's the full configuration for term. Also, I mistakenly thought that this is a variable for term-mode, when it's actually a variable defined in multi-term. However, if I place this in the multi-term configuration it produces the same behavior

(use-package term
  :after helm
  :config
  (defun expose-global-binding-in-term (binding)
    (define-key term-raw-map binding
      (lookup-key (current-global-map) binding)))
  (expose-global-binding-in-term (kbd "C-x"))
  (define-key term-raw-map (kbd "M-x") 'helm-M-x)
  (setq term-scroll-to-bottom-on-output t)
  (setq term-scroll-show-maximum-output t))

(use-package multi-term
  :bind (("<C-next>" . multi-term-next)
         ("<C-prior>" . multi-term-prev)
         ("<f1>" . multi-term))
  :config
  (require 'multi-term-ext)
  (setq multi-term-program "/bin/bash")
  (setq term-bind-key-alist
        (list (cons "C-c C-c" 'term-interrupt-subjob)
              (cons "C-z" 'term-stop-subjob)
              (cons "C-l" 'term-send-raw)
              (cons "C-r" 'term-send-raw)
              (cons "C-s" 'term-send-raw)
              (cons "M-f" 'term-send-forward-word)
              (cons "M-b" 'term-send-backward-word)
              (cons "C-c C-j" '(lambda () (interactive)
                                 (term-line-mode)
                                 (evil-normal-state)))
              (cons "C-c C-k" '(lambda () (interactive)
                                 (evil-emacs-state)
                                 (term-char-mode)
                                 (end-of-buffer)))
              (cons "M-DEL" 'term-send-backward-kill-word)
              (cons "M-d" 'term-send-forward-kill-word)
              (cons "<C-left>" 'term-send-backward-word)
              (cons "<C-right>" 'term-send-forward-word)
              (cons "C-y" 'term-paste))))

Edit 2

I've modified the multi-term configuration so that these keybindings are outside of term-bind-key-alist but I'm still getting the same behavior.

(use-package multi-term
  :bind (("<C-next>" . multi-term-next)
         ("<C-prior>" . multi-term-prev)
         ("<f1>" . multi-term)
         ("C-c C-j" . (lambda () (interactive)
                        (term-line-mode)
                        (evil-normal-state)))
         ("C-c C-k" . (lambda () (interactive)
                        (evil-emacs-state)
                        (term-char-mode)
                        (end-of-buffer))))
  :config
  (require 'multi-term-ext)
  (setq multi-term-program "/bin/bash")
  (setq term-bind-key-alist
    (list (cons "C-c C-c" 'term-interrupt-subjob)
          (cons "C-z" 'term-stop-subjob)
          (cons "C-l" 'term-send-raw)
          (cons "C-r" 'term-send-raw)
          (cons "C-s" 'term-send-raw)
          (cons "M-f" 'term-send-forward-word)
          (cons "M-b" 'term-send-backward-word)
          (cons "M-DEL" 'term-send-backward-kill-word)
          (cons "M-d" 'term-send-forward-kill-word)
          (cons "<C-left>" 'term-send-backward-word)
          (cons "<C-right>" 'term-send-forward-word)
          (cons "C-y" 'term-paste))))

Edit 3

The version below now works. The trick, as pointed out by Phil in his answer, is that term-bind-key-alist doesn't bind keys for term-mode-map (used by term-line-mode). I believe it only binds keys for term-raw-map, or some combination of that and term-raw-escape-map. The reason that term-char-mode was still called (and the other two functions weren't) when using "C-c C-k" in term-line-mode was that it's the default keybinding in term-line-mode.

(use-package term
  :after (helm evil)
  :bind (:map term-mode-map
              ("C-c C-k" . (lambda () (interactive)
                             (evil-emacs-state)
                             (term-char-mode)
                             (goto-char (point-max)))))
  :config
  (defun expose-global-binding-in-term (binding)
    (define-key term-raw-map binding
      (lookup-key (current-global-map) binding)))
  (expose-global-binding-in-term (kbd "C-x"))
  (define-key term-raw-map (kbd "M-x") 'helm-M-x)
  (setq term-scroll-to-bottom-on-output t)
  (setq term-scroll-show-maximum-output t))

(use-package multi-term
  :bind (("<C-next>" . multi-term-next)
         ("<C-prior>" . multi-term-prev)
         ("<f1>" . multi-term))
  :config
  (require 'multi-term-ext)
  (setq multi-term-program "/bin/bash")
  (setq term-bind-key-alist
        (list (cons "C-c C-c" 'term-interrupt-subjob)
              (cons "C-z" 'term-stop-subjob)
              (cons "C-l" 'term-send-raw)
              (cons "C-r" 'term-send-raw)
              (cons "C-s" 'term-send-raw)
              (cons "M-f" 'term-send-forward-word)
              (cons "M-b" 'term-send-backward-word)
              ;; I've defined "C-c C-k" in term's configuration since it must be defined in
              ;; term-mode-map.  It could be placed here as a define-key in config, but this way we
              ;; get to keep use-package's typical syntax.
              (cons "C-c C-j" '(lambda () (interactive)
                                 (term-line-mode)
                                 (evil-normal-state)))
              (cons "M-DEL" 'term-send-backward-kill-word)
              (cons "M-d" 'term-send-forward-kill-word)
              (cons "<C-left>" 'term-send-backward-word)
              (cons "<C-right>" 'term-send-forward-word)
              (cons "C-y" 'term-paste))))
1

Emacs 26.1 does not define term-bind-key-alist. If you don't show us what you're doing with that, we can't see what the problem is.

That said, it sounds like you're only binding keys in term-raw-map, and not in term-mode-map.

n.b. For clarity, I do not believe "lambda only calls 1 of several functions called in body" is something which is happening. I think you'll find that your custom commands are not being called at all when you are in line mode (because the keys sequence in question is not bound to your custom command in that scenario).


Try this.

(with-eval-after-load "term"
  (require 'evil)

  (defun my-term-line-mode ()
    "Switch to `term-line-mode' and enable `evil-normal-state'."
    (interactive)
    (term-line-mode)
    (evil-normal-state))

  (defun my-term-char-mode ()
    "Switch to `term-char-mode' and enable `evil-emacs-state'."
    (interactive)
    (term-char-mode)
    (evil-emacs-state))

  (define-key term-mode-map (kbd "C-c C-j") 'my-term-line-mode)
  (define-key term-mode-map (kbd "C-c C-k") 'my-term-char-mode)
  (define-key term-raw-escape-map (kbd "C-j") 'my-term-line-mode)
  (define-key term-raw-escape-map (kbd "C-k") 'my-term-char-mode))
  • I've edited the original post to add more context. Also please see my note about this actually being a variable for multi-term. My mistake for incorrectly assuming this was part of term mode. – MattHusz Feb 23 at 7:41
  • In that case it sounds like multi-term is using it to bind keys in term-raw-map, and not in term-mode-map. I suggest that you ignore this variable, and just bind the keys you want in the keymaps you need using the standard define-key method. – phils Feb 23 at 8:55
  • i.e. I suspect the answer to your question is simply that term-bind-key-alist doesn't actually do what you thought it did. – phils Feb 23 at 8:56
  • I pulled these keybindings out of term-bind-key-alist and edited the question to reflect that. Unfortunately the behavior seems to be identical. – MattHusz Feb 23 at 18:20
  • I've no idea what use-package does with that new config, but it will again surely only bind the keys in (at most) one of the two keymaps you're interested in. You need to bind these particular key sequences in both term-mode-map and term-raw-map, because they need to work in both line mode and char mode. – phils Feb 23 at 22:12

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.