millejoh / emacs-ipython-notebook

Jupyter notebook client in Emacs
http://millejoh.github.io/emacs-ipython-notebook/
GNU General Public License v3.0
1.47k stars 122 forks source link

ein:notebook-mode-map is not bound upon startup #714

Closed matsievskiysv closed 4 years ago

matsievskiysv commented 4 years ago

I had some custom keybindings in ein:notebook-mode-map setup via use-package :bind. Recently, use-package started complaining that ein:notebook-mode-map does not exist. It seems that ein:notebook-mode-map is defined upon first loading of a notebook which is unfortunate, since there is no way to define keybindings besides setting them up using hooks.

Tested this behavior with the following code:

> emacs -Q -L $(find $(pwd) -maxdepth 1 -type d | paste -sd '!' | sed 's/!/ -
L /g')
(require 'ein)
(boundp 'ein:notebook-mode-map) ;; is nil
GNU Emacs 26.3 (build 2, x86_64-pc-linux-gnu, GTK+ Version 3.24.11) of 2019-09-22, modified by Debian
ein 20200514.2040
dickmao commented 4 years ago

You should use-package on ein-notebook, not ein. Here's an example.

https://github.com/millejoh/emacs-ipython-notebook/issues/174#issuecomment-596225112

matsievskiysv commented 4 years ago

I could't get the provided example to work. Souldn't it be

(use-package ein-notebook
  :ensure ein)

Otherwise use-package couldn't find a package.

dickmao commented 4 years ago

I'm confused. If you run the following,

emacs -Q -f package-initialize --eval "(setq debug-on-error t)" --eval "(use-package ein-notebook :bind (:map ein:notebook-mode-map (\"C-t\" . my-test)) :init (defun my-test () (interactive) (message \"worked\")))"

then open a notebook, and type C-t, it should say "worked" in the minibuffer. `

matsievskiysv commented 4 years ago

@dickmao This is a slightly modified version of your minimal example:

> emacs -Q --eval "(setq debug-on-error t)" --eval "(setq package-load-list '((bind-key t) (use-package t)))" -f package-initialize --eval "(use-package ein-notebook :ensure t :bind (:map ein:notebook-mode-map (\"C-t\" . my-test)) :init (defun my-test () (interactive) (message \"worked\")))"

This starts debugger at point:

Debugger entered--Lisp error: (error "Package ‘ein-notebook-’ is unavailable")

Your example worked because (package-initialize) loads all packages in ~/.emacs.d/elpa by default and you have ein in there.

dickmao commented 4 years ago

So you use use-package but you don't run (package-initialize)? That is very odd to me. I know that if I don't have (package-initialize) in my init.el then emacs will forcefully reinsert it. So I do not fight emacs. I always assume (package-initialize).

Does this help? I am just guessing now.

(use-package ein
  :config (use-package ein-notebook
        :bind (:map ein:notebook-mode-map ("C-t" . my-test))
        :init (defun my-test () (interactive) (message "worked"))))
matsievskiysv commented 4 years ago

I don't think that package-initialize should be used with use-package. use-package is designed to lazy-load packages. package-initialize would force load them. Try your example this way:

> rm -rf ~/.emacs.d/elpa/ein-*
> emacs -Q -f package-initialize --eval "(setq debug-on-error t)" --eval "(use-package ein-notebook :ensure t :bind (:map ein:notebook-mode-map (\"C-t\" . my-test)) :init (defun my-test () (interactive) (message \"worked\")))"

Note that I added :ensure t option. Without it use-package will defer package loading.

Does this help? I am just guessing now.

As I mentioned before, this helps (key binding also works):

(use-package ein-notebook
  :ensure ein)
dickmao commented 4 years ago

Much to my surprise, the author of use-package does not package-initialize.

But until told otherwise, I believe (package-initialize) only picks up the so-called autoloads (those sentinel functions that trigger the lazy load), and would not actually load any packages. So when I start emacs, M-: (featurep 'ein) returns nil until I actually start using EIN.

Your :ensure ein does seem to work but I needed to add melpa to package-archives.

rm -rf ~/.emacs.d/elpa/ein-*
emacs -Q -f package-initialize --eval "(add-to-list 'package-archives '(\"melpa\" . \"http://melpa.org/packages/\"))" --eval "(setq debug-on-error t)" --eval "(use-package ein-notebook :ensure ein :bind (:map ein:notebook-mode-map (\"C-t\" . my-test)) :init (defun my-test () (interactive) (message \"worked\")))"

The emacs packaging is a royal mess, and autoloads are very tricky. People seem to swear by use-package, and while it is syntactically nice, it doesn't really un-complicate the packaging semantics. In some sense, use-package only makes the semantics more opaque.