Open arkhan opened 5 years ago
Good question.
Now, I've been using use-package
for a long time. Have you ever been stressed by use-package
?
For example, can you add a keyword like :doc
in leaf
, which is the "Do nothing" keyword to use-package
?
When I tried it, I couldn't do it. Please try it. This is the first step in understanding use-package
.
That in leaf
's implementation is very easy.
https://github.com/conao3/leaf.el/blob/da616cda1418ba4ea15d75944e5c52fd7cface6e/leaf.el#L212
It simply returns the previous value as its own value. If you're not familiar with recursive functions, it might be difficult to read, but it's an important concept for programmers, even if it's not in lisp context.
The key difference for end users is probably the difference between the :bind
and :custom
keywords. The :bind
keyword of use-package breaks indentation when there is no global assignment.
I knew that, so I was able to design a receipt format that would not break indents.
(use-package term
:bind (("C-c t" . term)
:map term-mode-map
("M-p" . term-send-up) ; good indent
("M-n" . term-send-down)))
(use-package term
:bind (:map term-mode-map
("M-p" . term-send-up) ; indentation broken
("M-n" . term-send-down)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf term
:bind (("C-c t" . term)
(term-mode-map ; or keyword :term-mode-map
("M-p" . term-send-up)
("M-n" . term-send-down))))
(leaf term
:bind ((term-mode-map ; or keyword :term-mode-map
("M-p" . term-send-up)
("M-n" . term-send-down))))
The :custom
keyword in use-package
is disproportionate to keywords that accept dotted pairs, such as :mode
and :hook
etc. How many people set custom-set-variables
's third argument? The leaf
decided that it was more important to harmonize the whole than to consider the unusual scene.
(p (use-package org
:bind (("M-o o c" . org-capture)
("M-o o a" . org-agenda)
("M-o o l" . org-store-link)))) ; expect dot-pair
;; => (progn
;; (unless (fboundp 'org-capture) (autoload #'org-capture "org" nil t))
;; (unless (fboundp 'org-agenda) (autoload #'org-agenda "org" nil t))
;; (unless (fboundp 'org-store-link) (autoload #'org-store-link "org" nil t))
;; (bind-keys :package org
;; ("M-o o c" . org-capture)
;; ("M-o o a" . org-agenda)
;; ("M-o o l" . org-store-link)))
(p (use-package real-auto-save
:hook (find-file . real-auto-save-mode))) ; of cource expect dot-pair
;; => (progn
;; (unless (fboundp 'real-auto-save-mode) (autoload #'real-auto-save-mode "real-auto-save" nil t))
;; (add-hook 'find-file-hook #'real-auto-save-mode))
(p (use-package real-auto-save
:custom ((real-auto-save-interval . 0.3)))) ; error when passing pair!??
;; => Debugger entered--Lisp error: (wrong-type-argument listp 0.3)
(p (use-package real-auto-save
:custom ((real-auto-save-interval 0.3)))) ; expect list!??
;; => (progn
;; (customize-set-variable 'real-auto-save-interval 0.3 "Customized with use-package real-auto-save")
;; (require 'real-auto-save nil nil))
(p (leaf org
:bind (("M-o o c" . org-capture)
("M-o o a" . org-agenda)
("M-o o l" . org-store-link)))) ; expect dot-pair
;; => (prog1 'org
;; (autoload #'org-capture "org" nil t)
;; (autoload #'org-agenda "org" nil t)
;; (autoload #'org-store-link "org" nil t)
;; (leaf-keys
;; (("M-o o c" . org-capture)
;; ("M-o o a" . org-agenda)
;; ("M-o o l" . org-store-link))))
(p (leaf real-auto-save
:hook (find-file-hook . real-auto-save-mode))) ; expect dot-pair
;; => (prog1 'real-auto-save
;; (autoload #'real-auto-save-mode "real-auto-save" nil t)
;; (add-hook 'find-file-hook #'real-auto-save-mode))
(p (leaf real-auto-save
:custom ((real-auto-save-interval . 0.3)))) ; expect dot-pair
;; => (prog1 'real-auto-save
;; (custom-set-variables
;; '(real-auto-save-interval 0.3 "Customized with leaf in real-auto-save block")))
Finally, the clean internal implementation of leaf
may certainly be meaningless to the end user. However, the ease of internal implementation makes it easier for developers to benefit from additional keywords. In fact, it supports more keywords than use-package and you don't have to be me to do that. Because it is easy.
use-package-keywords (leaf-available-keywords)
;;=> (:disabled ;;=> (:disabled
;; :load-path ;; :convert-defaults
;; :requires ;; :leaf-protect
;; :defines ;; :load-path
;; :functions ;; :load-path*
;; :preface ;; :leaf-autoload
;; :if ;; :defun
;; :when ;; :defvar
;; :unless ;; :leaf-defun
;; :no-require ;; :leaf-defvar
;; :catch ;; :preface
;; :after ;; :when
;; :custom ;; :unless
;; :custom-face ;; :if
;; :bind ;; :doc
;; :bind* ;; :req
;; :bind-keymap ;; :tag
;; :bind-keymap* ;; :added
;; :interpreter ;; :comment
;; :mode ;; :file
;; :magic ;; :url
;; :magic-fallback ;; :emacs<
;; :hook ;; :emacs<=
;; :commands ;; :emacs=
;; :init ;; :emacs>
;; :defer ;; :emacs>=
;; :demand ;; :package
;; :load ;; :ensure
;; :config ;; :feather
;; :blackout) ;; :straight
;; :el-get
;; :defaults
;; :after
;; :commands
;; :bind
;; :bind*
;; :mode
;; :interpreter
;; :magic
;; :magic-fallback
;; :hook
;; :advice
;; :advice-remove
;; :pre-setq
;; :pl-pre-setq
;; :auth-pre-setq
;; :custom
;; :custom*
;; :pl-custom
;; :auth-custom
;; :custom-face
;; :init
;; :require
;; :global-minor-mode
;; :hydra
;; :transient
;; :combo
;; :combo*
;; :smartrep
;; :smartrep*
;; :chord
;; :chord*
;; :mode-hook
;; :leaf-defer
;; :setq
;; :setq-default
;; :pl-setq
;; :auth-setq
;; :pl-setq-default
;; :auth-setq-default
;; :delight
;; :diminish
;; :blackout
;; :grugru
;; :config
;; :defer-config)
https://github.com/conao3/leaf-keywords.el/pull/35
This PR looks like a lot of changed lines, but in effect, with 10 line changes, he could add the keyword :straight
. Is this kind of trick possible for use-package
?
The leaf was developed by myself only. This is the easiest way to solve the copyright problems that the FSF requires. (see reddit and php-mode wiki)
I think that it is a loss of the world that use-package
is not attached to Emacs as a standard.
leaf
aims to be an Emacs standard attachment, which I hope will become the package configuration standard in Emacs. leaf
has this future outlook, so moving from use-package
to leaf
is a good choice.
Oops. p
is here.
(defmacro p (form)
"Output expanded form of given FORM."
`(progn
(pp (macroexpand-1 ',form))
nil))
As a relatively new user to Emacs, would you recommend that I start off with use-package first? I'm not familiar with use-package and don't quite know what you're talking about when it comes to the short-comings of use-package. I would rather build my init.el from the ground up the right way, and leaf.el seems to be the way to do it, but I am not really grasping how to use it since it's always referenced to use-package.
OK. If you are not familiar with Emacs or use-package
, you should start by looking at my init.el and get started. This will be good practice for leaf.el.
And if you have any trouble, ask me anything. I have a dedicated Slack ready for that. README has invitation link.
Have you seen how use-package implements its own keywords? It does so via an extensible mechanism as well. For example, :magic
is implemented using:
(defalias 'use-package-normalize/:magic 'use-package-normalize-mode)
(defalias 'use-package-autoloads/:magic 'use-package-autoloads-mode)
(defun use-package-handler/:magic (name _keyword arg rest state)
(use-package-handle-mode name 'magic-mode-alist arg rest state))
Any keyword can be added by following this function naming pattern, and then inserting the new keyword into use-package-keywords
so that use-package
can understand what its priority should be.
I've seen it, and I'm a use-package
contributor.
As stated in the README, I've been using use-package for 2.5 years and really still love it, and it's revolutionized the way Emacs configuration.
But use-package
has a long history, the code has become bloated.
Some of the stresses I've felt are as follows.
keyword implementation
As far as I'm concerned, your implementation of :magic
is problematic.
Like :magic
, many functions are defined by defalias
, and we need to care a lot to change use-package-normalize-mode
.
Can you easily figure out which keywords depend on use-package-normalize-mode
, for example?
If even your keywords rely on defalias
, it's likely that other user-implemented packages also have aliases for use-package-normalize-mode
.
In my leaf
, I can see at a glance which keywords are sharing code with :mode
.
https://github.com/conao3/leaf.el/blob/de916f86ea5391c205c03d04fec027bf00bb6d61/leaf.el#L210-L211
Grammar of keywords
Perhaps by a common-lisp analogy, the :disabled
keyword is activated by simply writing the keyword.
As a result, even if I give nil
to :disabled
, that use-package will be converted to nil even if I give t
.
This is a little odd behavior that is different from keywords like :defer
, :no-require
and :when
.
A :after
can receive a package symbol or a list of them, but not the :ensure
keyword.
:hook
and :bind
can accept a dot pair or a list of them, but not a :custom
keyword.
bind
has a broken indentation in some cases of accepted formats.
(use-package term
:bind (("C-c t" . term)
:map term-mode-map
("M-p" . term-send-up) ; good indent
("M-n" . term-send-down)))
(use-package term :bind (:map term-mode-map ("M-p" . term-send-up) ; indent broken ("M-n" . term-send-down)))
These problems can no longer be fixed, as `use-package` has a million DLs in MELPA and many users' init.el rely on it.
These may be small reasons, but they were enough reasons for me to develop a package called `leaf` from scratch.
Having developed from scratch, my leaf is made up of code only for people who have signed up for FSF.
Your `use-package` is useful, and this should be a standard attachment to Emacs.
There are a lot of hurdles for `leaf` to be attached to Emacs, but `leaf` has at least cleared the licensing issues.
Also, there are already many more features and related packages than `use-package`.
```emacs-lisp
(length use-package-keywords)
;;=> 34
(length (leaf-available-keywords))
;;=> 72
The only use-package
related packages are those related to keywords, but leaf aggregates them into leaf-keywords
. (MELPA - use-package)
There are many unique and interesting packages related to leaf
. (MELPA - leaf)
The point about indentation is moot as of https://github.com/emacs-mirror/emacs/commit/1b2a881c9b3ef9158cbf28a65b9e94c2dbc6cec2
(use-package term
:bind ( :map term-mode-map
("M-p" . term-send-up)
("M-n" . term-send-down)))
weird hack. Why do we add one space indent for all other lines by remove some global binding?
(use-package term
:bind (("C-c t" . term)
:map term-mode-map
("M-p" . term-send-up) ; good indent
("M-n" . term-send-down)))
;; remove global binding; ("C-c t" . term)
(use-package term
:bind (:map term-mode-map
("M-p" . term-send-up) ; bad indent
("M-n" . term-send-down)))
(use-package term
:bind ( :map term-mode-map
("M-p" . term-send-up) ; better indent?
("M-n" . term-send-down)))
see this diff. We get a diff where it's not relevant. I am not convinced that this is a good choice.
$ diff -u use-package.el.orig use-package.el
--- use-package.el.orig 2020-10-09 17:17:56.880058593 +0900
+++ use-package.el 2020-10-09 17:18:17.720058370 +0900
@@ -1,5 +1,4 @@
(use-package term
- :bind (("C-c t" . term)
- :map term-mode-map
- ("M-p" . term-send-up)
- ("M-n" . term-send-down)))
+ :bind ( :map term-mode-map
+ ("M-p" . term-send-up)
+ ("M-n" . term-send-down)))
see leaf
side. This is so simple! Just remove ("C-c t" . term)
.
(leaf term
:bind (("C-c t" . term)
(term-mode-map
("M-p" . term-send-up)
("M-n" . term-send-down))))
;; remove global binding; ("C-c t" . term)
(leaf term
:bind ((term-mode-map
("M-p" . term-send-up)
("M-n" . term-send-down))))
use-package
is actually not needed at runtime https://github.com/jwiegley/use-package#use-packageel-is-no-longer-needed-at-runtime.
But from leaf
example, it seems leaf
is not and the package has to be loaded for leaf-handler-package
and leaf-keys
to be usable, which increases startup time.
(cort-deftest-with-macroexpand leaf/bind
'(
;; cons-cell will be accepted
((leaf macrostep
:ensure t
:bind ("C-c e" . macrostep-expand))
(prog1 'macrostep
(unless (fboundp 'macrostep-expand) (autoload #'macrostep-expand "macrostep" nil t))
(declare-function macrostep-expand "macrostep")
(leaf-handler-package macrostep macrostep nil)
(leaf-keys (("C-c e" . macrostep-expand)))))))
Can leaf
provide no runtime requirement like use-package
?
The test is tested the Sexp expanded by macroexpand-1
so please use macroexpand-all
to expand all macros.
There're no leaf
functions after expanded form.
(ppp-macroexpand-all
(leaf macrostep
:ensure t
:bind ("C-c e" . macrostep-expand)))
;;=> (prog1 'macrostep
;; (condition-case err
;; (progn
;; (if (fboundp 'macrostep-expand)
;; nil
;; (autoload #'macrostep-expand "macrostep" nil t))
;; nil
;; (if (package-installed-p 'macrostep)
;; nil
;; (if (assoc 'macrostep package-archive-contents)
;; nil
;; (package-refresh-contents))
;; (condition-case _err
;; (package-install 'macrostep)
;; (error
;; (condition-case err
;; (progn
;; (package-refresh-contents)
;; (package-install 'macrostep))
;;
;; (error
;; (display-warning 'leaf
;; (format "In `macrostep' block, failed to :package of macrostep. Error msg: %s"
;; (error-message-string err))))))))
;; (let* ((old (lookup-key global-map
;; (kbd "C-c e")))
;; (value (list 'global-map "C-c e" 'macrostep-expand
;; (and old
;; (not (numberp old))
;; old))))
;; (setq leaf-key-bindlist (cons value leaf-key-bindlist))
;; (define-key global-map
;; (kbd "C-c e")
;; 'macrostep-expand)))
;;
;; (error
;; (display-warning 'leaf
;; (format "Error in `macrostep' block. Error msg: %s"
;; (error-message-string err))))))
In use-package
you can use the following construction for correct indentation of the :bind keyword
(use-package tex
:ensure auctex
:bind ((:map latex-mode-map)
("C-c x" . 'latex-kill-section)))
Regards,
I found this package and it seemed interesting, but I had a question, what are the differences or advantages over use-package?
Great job