Skip to content


Subversion checkout URL

You can clone with
Download ZIP


C-<up> binded to merlin-type-enclosing-go-up #129

bobot opened this Issue · 7 comments

4 participants



Just after an update I fall into this bug:

Actions to reproduce the bug:

  • load an .ml with the merlin mode activated (opam version 1.4.1)
  • go somewhere in the buffer
  • type C-<up>

Current result:

  • the cursor doesn't move

Expected result:

  • the cursor move "fastly" up

The commit 6610145 binded C-<up> to merlin-type-enclosing-go-up, which seems to do nothing before C-t ('merlin-type-expr) is run, instead of merlin-prev-phrase-or-go-up. Normally C-<up> is binded to backward-paragraph.

Possible solution: keep the default binding for C-<up>

Best solution: Just after a C-t make C-<up> and C-<down> control the typed-region, after any other keystrokes exit this behavior. Outside the "C-t-mode" C-<up> is backward-paragraph or something better.


@trefis trefis closed this

Ok its your choice, but:
1. auto-complete which is used for completion uses bindings that depend on the context
2. C-<up> is present in numerous tutorials. Less typing for an usual command is rational. And backward-paragraph is more often used than merlin-type-enclosing-go-up.
3. For reference here what can be used in a .emacs to recover the default:

(eval-after-load 'merlin
  '(progn (define-key merlin-mode-map (kbd "C-<up>") nil)
          (define-key merlin-mode-map (kbd "C-<down>") nil)))
@trefis trefis reopened this

Thanks for taking my remarks into account.

I didn't know how to make the contextual command, so I learned from popup.el. It can be useful for someone else even if the result is not very robust. After mymerlin-type-enclosing C-<up>, C-<down> move to bigger or smaller expression and C-<left>, C-<right> move to the expression on the left or right (not robust, ie syntaxic not semantic) and each time print the type. Funny but not robust.

(defun my-read-key-sequence (keymap &optional prompt timeout)
  (catch 'timeout
    (let ((timer (and timeout
                      (run-with-timer timeout nil
                                      (lambda ()
                                        (if (zerop (length (this-command-keys)))
                                            (throw 'timeout nil))))))
          (old-global-map (current-global-map))
          (temp-global-map (make-sparse-keymap))
          (overriding-terminal-local-map (make-sparse-keymap)))
      (substitute-key-definition 'keyboard-quit 'keyboard-quit
                                 temp-global-map old-global-map)
      (define-key temp-global-map [menu-bar] (lookup-key old-global-map [menu-bar]))
      (define-key temp-global-map [tool-bar] (lookup-key old-global-map [tool-bar]))
      (set-keymap-parent overriding-terminal-local-map keymap)
      (if (current-local-map)
          (define-key overriding-terminal-local-map [menu-bar]
            (lookup-key (current-local-map) [menu-bar])))
            (use-global-map temp-global-map)
            (with-temp-message prompt
              (read-key-sequence nil)))
        (use-global-map old-global-map)
        (if timer (cancel-timer timer))))))

(defun* my-menu-event-loop (keymap)
  (block nil
    (while t
      (setq key (my-read-key-sequence keymap "type:"))
      (if (null key)
        (if (eq (lookup-key (current-global-map) key) 'keyboard-quit)
        (setq binding (lookup-key keymap key))
         ((eq binding 'up)
         ((eq binding 'down)
         ((eq binding 'left)
            (let ((data (elt merlin-enclosing-types merlin-enclosing-offset)))
              (if (cddr data)
                    (goto-char (car (cdr data)))
         ((eq binding 'right)
            (let ((data (elt merlin-enclosing-types merlin-enclosing-offset)))
              (if (cddr data)
                    (goto-char (cdr (cdr data)))
         ((commandp binding)
          (call-interactively binding))

(defvar my-type-enclosing-map
  (let ((test-map (make-sparse-keymap)))
    (define-key test-map (kbd "C-<up>") 'up)
    (define-key test-map (kbd "C-<down>") 'down)
    (define-key test-map (kbd "C-<left>") 'left)
    (define-key test-map (kbd "C-<right>") 'right)

(defun mymerlin-type-enclosing ()
  "Print the type of the expression under point (or of the region, if it exists)."
    (if (region-active-p)
      (if (merlin-type-enclosing-query)
          (progn (merlin-type-enclosing-go-up)
                 (my-menu-event-loop my-type-enclosing-map)

Just to follow up on this, I was also surprised that merlin-mode overrides the C-up and C-down behavior. I rely on the default bindings for those key combinations quite frequently for navigation in a variety of source languages (and they seem to be behave consistently regardless of file type). That they "broke" almost made me give up on using merlin until I found this bug report with the suggested fix.


Forgot about this one. Apparently #170 doesn't list every keybinding we stole. I'll be sure to remember it when I fix the ones of #170.

To answer François (which we should have done months ago) I personally don't like the idea of contextual keybindings (but it may be the natural way to do things in emacs? Idk), and it makes the code more "complex" ; so I'd rather avoid it.
Someone disagrees?

@trefis trefis added the emacs mode label

@gsg introduced an overlay map in 1a4e2dc fixing this problem.

@def-lkb def-lkb closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.