leaf.el
is yet another use-package.
leaf-keywords.el
add additional keywords for leaf.el.
leaf.el
and leaf-keywords.el
can install with package.el from MELPA, so sample instration code is below.
In order to work from Emacs-22, the package manager and the key binding manager
that accompanies leaf
must also be developed with the assumption that they will work from Emacs-22.
I have plans to develop it, but it’s not finished yet.
Package to be developed
- feather.el instead of
package.el
- leaf-key.el instead of
bind-key
-> (Achieved! Nowleaf
builtin)
(prog1 "prepare leaf"
(prog1 "package"
(custom-set-variables
'(package-archives '(("org" . "https://orgmode.org/elpa/")
("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/"))))
(package-initialize))
(prog1 "leaf"
(unless (package-installed-p 'leaf)
(unless (assoc 'leaf package-archive-contents)
(package-refresh-contents))
(condition-case err
(package-install 'leaf)
(error
(package-refresh-contents) ; renew local melpa cache if fail
(package-install 'leaf))))
(leaf leaf-keywords
:ensure t
:config (leaf-keywords-init)))
(prog1 "optional packages for leaf-keywords"
;; optional packages if you want to use :hydra, :el-get,,,
(leaf hydra :ensure t)
(leaf el-get :ensure t
:custom ((el-get-git-shallow-clone . t)))))
Put leaf.el
at any folder added load-path
.
Then (require 'leaf)
and use like use-pacakge
.
(In this example, you installed/loaded leaf directly, so you can configure package.el
using leaf
.)
;; add to load-path
;; (locate-user-emacs-file "site-lisp/leaf.el")
;; => "~/.emacs.d/local/26.1/site-lisp/leaf.el"
(prog1 "leaf"
(add-to-list 'load-path (locate-user-emacs-file "site-lisp/leaf.el"))
(require 'leaf)
(leaf package
:custom ((package-archives . '(("org" . "https://orgmode.org/elpa/")
("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/"))))
:config
(package-initialize))
(leaf leaf-keywords
:ensure t
:config (leaf-keywords-init))
(prog1 "optional packages for leaf-keywords"
;; optional packages if you want to use :hydra, :el-get,,,
(leaf hydra :ensure t)
(leaf el-get :ensure t
:custom ((el-get-git-shallow-clone . t)))))
Use leaf
in your init.el like use-package
.
You declaratively tell the leaf
to configure the package using special keywords.
leaf
converts your declaration into Elisp for Emacs to understand, and Emacs executes it to configure the package.
These keywords are buildin. Info is here.
(cort-deftest-with-macroexpand leaf/package
'(((leaf leaf
:package t
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-init)))
((leaf leaf
:package t leaf-browser
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-handler-package leaf leaf-browser nil)
(leaf-init)))
((leaf leaf
:package feather leaf-key leaf-browser
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf feather nil)
(leaf-handler-package leaf leaf-key nil)
(leaf-handler-package leaf leaf-browser nil)
(leaf-init)))))
(cort-deftest-with-macroexpand leaf/handler-package
'(((leaf macrostep :ensure t)
(prog1 'macrostep
(leaf-handler-package macrostep macrostep nil))
((leaf-handler-package macrostep macrostep nil)
(unless
(package-installed-p 'macrostep)
(condition-case err
(progn
(unless (assoc 'macrostep package-archive-contents)
(package-refresh-contents))
(package-install 'macrostep))
(error
(condition-case err
(progn
(package-refresh-contents)
(package-install 'macrostep))
(error
(leaf-error "In `macrostep' block, failed to :package of macrostep. Error msg: %s"
(error-message-string err)))))))))))
:feather
keyword provede frontend of feather
.
Like most :package
, but use feather-add-after-installed-hook-sexp
to set up an S-exp like :config
so that feather expects it.
If a leaf block specifies multiple packages to install, the S-exp is set to execute after the last package is installed.
(cort-deftest-with-macroexpand leaf/feather
'(
;; 't will be converted leaf--name
((leaf leaf
:init (leaf-pre-init)
:feather t
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(feather-add-after-installed-hook-sexp leaf
(leaf-pre-init)
(leaf-init))))
;; multi symbols will be accepted
((leaf leaf
:init (leaf-pre-init)
:feather leaf leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-handler-package leaf leaf-polyfill nil)
(feather-add-after-installed-hook-sexp leaf-polyfill
(leaf-pre-init)
(leaf-init))))
;; multi symbols in list will be accepted
((leaf leaf
:feather (feather leaf-key leaf-browser)
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf feather nil)
(leaf-handler-package leaf leaf-key nil)
(leaf-handler-package leaf leaf-browser nil)
(feather-add-after-installed-hook-sexp leaf-browser
(leaf-init))))
;; multi keyword will be accepted
((leaf leaf
:init (leaf-pre-init)
:feather t
:feather leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-handler-package leaf leaf-polyfill nil)
(feather-add-after-installed-hook-sexp leaf-polyfill
(leaf-pre-init)
(leaf-init))))
;; keywords such as :preface that expand before :feather
;; are not registered in the hook of feather
((leaf leaf
:preface (leaf-preface)
:init (leaf-pre-init)
:feather t
:config (leaf-init))
(prog1 'leaf
(leaf-preface)
(leaf-handler-package leaf leaf nil)
(feather-add-after-installed-hook-sexp leaf
(leaf-pre-init)
(leaf-init))))))
:el-get
provide frontend of el-get-bundle
.
If you specify t
, leaf assumes that you specified the name of the leaf-block.
Given a list, the arguments are passed as is to the el-get-bundle
.
(cort-deftest-with-macroexpand leaf/el-get
'(((leaf leaf
:init (leaf-pre-init)
:el-get t
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get leaf leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get t
:el-get leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get t leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get (zenburn-theme
:url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle zenburn-theme :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get
(yaicomplete
:url "https://github.com/tarao/elisp.git"
:features yaicomplete)
(zenburn-theme
:url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
(kazu-yamamoto/Mew :name mew :build ("./configure" "make"))
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle yaicomplete :url "https://github.com/tarao/elisp.git" :features yaicomplete)
(el-get-bundle zenburn-theme :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
(el-get-bundle kazu-yamamoto/Mew :name mew :build ("./configure" "make"))))
(leaf-pre-init)
(leaf-init)))))
:straight
provides a frontend for straight-use-package
.
If you specify t
, leaf assumes that you specified the name of the leaf-block.
Given a list, the arguments are passed as is to straight-use-package
.
(cort-deftest-with-macroexpand leaf/straight
'(((leaf leaf
:init (leaf-pre-init)
:straight t
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight leaf leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight t
:straight leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight t leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight (zenburn-theme :type git :host github :repo "fake/fake")
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package '(zenburn-theme :type git :host github :repo "fake/fake"))))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight
(zenburn-theme :type git :host github :repo "fake/fake")
(yaicomplete :type git :host github :repo "fake/faker")
(mew :type git :host gitlab :repo "fake/fakest" :no-build)
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package '(zenburn-theme :type git :host github :repo "fake/fake"))
(straight-use-package '(yaicomplete :type git :host github :repo "fake/faker"))
(straight-use-package '(mew :type git :host gitlab :repo "fake/fakest" :no-build))))
(leaf-pre-init)
(leaf-init)))))
:ensure-system-package
provides a frontend for system-packages.
If you specify t
, leaf assumes that you specified the name of the leaf-block.
(cort-deftest-with-macroexpand leaf/ensure-system-package
'(
;; specify a symbol to set to autoload function
((leaf leaf
:ensure-system-package rg
:config (leaf-init))
(prog1 'leaf
(unless (executable-find "rg") (system-packages-install "rg"))
(leaf-init)))
;; multi symbols will be accepted
((leaf leaf
:ensure-system-package rg exa bat)
(prog1 'leaf
(unless (executable-find "rg") (system-packages-install "rg"))
(unless (executable-find "exa") (system-packages-install "exa"))
(unless (executable-find "bat") (system-packages-install "bat"))))
;; multi symbols in list will be accepted
((leaf leaf
:ensure-system-package (rg exa bat))
(prog1 'leaf
(unless (executable-find "rg") (system-packages-install "rg"))
(unless (executable-find "exa") (system-packages-install "exa"))
(unless (executable-find "bat") (system-packages-install "bat"))))
;; It is accepted even if you specify symbol and list at the same time
((leaf leaf
:ensure-system-package openssl (rg exa bat))
(prog1 'leaf
(unless (executable-find "openssl") (system-packages-install "openssl"))
(unless (executable-find "rg") (system-packages-install "rg"))
(unless (executable-find "exa") (system-packages-install "exa"))
(unless (executable-find "bat") (system-packages-install "bat"))))
;; if you specify t, use leaf--name
((leaf rg
:ensure-system-package t)
(prog1 'rg
(unless (executable-find "rg") (system-packages-install "rg"))))
;; Specify the package the package name for given symbol
((leaf leaf
:ensure-system-package (rg . ripgrep))
(prog1 'leaf
(unless (executable-find "rg") (system-packages-install "ripgrep"))))
;; Install package if the presence of file is nil
((leaf vterm
:ensure-system-package (("/usr/lib/libvterm.so" . libvterm)
(cmake libtool)))
(prog1 'vterm
(unless (file-exists-p "/usr/lib/libvterm.so") (system-packages-install "libvterm"))
(unless (executable-find "cmake") (system-packages-install "cmake"))
(unless (executable-find "libtool") (system-packages-install "libtool"))))
;; you can specify install shell command
((leaf vterm
:ensure-system-package (prettier . "npm i -g prettier-plugin-svelte"))
(prog1 'vterm
(unless (executable-find "prettier") (async-shell-command "npm i -g prettier-plugin-svelte"))))))
These keywords are buildin. Info is here.
(cort-deftest-with-macroexpand leaf/bind
'(((leaf macrostep
:package t
:bind (("C-c e" . macrostep-expand)))
(prog1 'macrostep
(autoload #'macrostep-expand "macrostep" nil t)
(leaf-handler-package macrostep macrostep nil)
(leaf-keys (("C-c e" . macrostep-expand)))))
((leaf macrostep
:package t
:bind ("C-c e" . macrostep-expand))
(prog1 'macrostep
(autoload #'macrostep-expand "macrostep" nil t)
(leaf-handler-package macrostep macrostep nil)
(leaf-keys
(("C-c e" . macrostep-expand)))))
((leaf color-moccur
:bind
("M-s O" . moccur)
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all)))))
((leaf color-moccur
:bind (("M-s O" . moccur)
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all)))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all)))))
((leaf color-moccur
:bind
("M-s" . nil)
("M-s o" . isearch-moccur)
("M-s i" . isearch-moccur-all))
(prog1 'color-moccur
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s")
("M-s o" . isearch-moccur)
("M-s i" . isearch-moccur-all)))))
((leaf color-moccur
:bind (("M-s" . nil)
("M-s o" . isearch-moccur)
("M-s i" . isearch-moccur-all)))
(prog1 'color-moccur
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s")
("M-s o" . isearch-moccur)
("M-s i" . isearch-moccur-all)))))
((leaf color-moccur
:bind
("M-s O" . moccur)
(:isearch-mode-map
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all)))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
(:isearch-mode-map
:package color-moccur
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))))
((leaf color-moccur
:bind
("M-s O" . moccur)
(:isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all)))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
(:isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))))
((leaf color-moccur
:bind (("M-s O" . moccur)
(:isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
(:isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))))
;; you also use symbol instead of keyword to specify keymap
((leaf color-moccur
:bind (("M-s O" . moccur)
(isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(leaf-keys (("M-s O" . moccur)
(isearch-mode-map
:package isearch
("M-o" . isearch-moccur)
("M-O" . isearch-moccur-all))))))))
(cort-deftest-with-macroexpand leaf/leaf-key
'(((leaf-key "C-M-i" 'flyspell-correct-wrapper)
(let* ((old (lookup-key global-map (kbd "C-M-i")))
(value `(("C-M-i" . global-map) flyspell-correct-wrapper ,(and old (not (numberp old)) old))))
(push value leaf-key-bindlist)
(define-key global-map (kbd "C-M-i") 'flyspell-correct-wrapper)))))
:hydra
provide frontend for hydra.
If you pass a list, you pass it to defhydra
, and if you pass a nested list, you pass each one to it.
The reason for using this keyword is that it automatically creates an autoload
statement.
(cort-deftest-with-macroexpand leaf/hydra
'(((leaf face-remap
:hydra (hydra-zoom
(global-map "<f2>")
"zoom"
("g" text-scale-increase "in")
("l" text-scale-decrease "out")))
(prog1 'face-remap
(autoload #'text-scale-increase "face-remap" nil t)
(autoload #'text-scale-decrease "face-remap" nil t)
(eval-after-load 'hydra
'(progn
(defhydra hydra-zoom
(global-map "<f2>")
"zoom"
("g" text-scale-increase "in")
("l" text-scale-decrease "out"))))))
((leaf yasnippet
:bind (:yas-minor-mode-map
("<f3>" . hydra-yas-primary/body)
("<f2>" . hydra-yas/body))
:hydra ((hydra-yas-primary
(:hint nil)
"yas-primary"
("i" yas-insert-snippet)
("n" yas-new-snippet)
("v" yas-visit-snippet-file))
(hydra-yas
(:color blue :hint nil)
"
^YASnippets^
--------------------------------------------
Modes: Load/Visit: Actions:
_g_lobal _d_irectory _i_nsert
_m_inor _f_ile _t_ryout
_e_xtra _l_ist _n_ew
_a_ll
"
("d" yas-load-directory)
("e" yas-activate-extra-mode)
("i" yas-insert-snippet)
("f" yas-visit-snippet-file :color blue)
("n" yas-new-snippet)
("t" yas-tryout-snippet)
("l" yas-describe-tables)
("g" yas/global-mode)
("m" yas/minor-mode)
("a" yas-reload-all))))
(prog1 'yasnippet
(autoload #'yas-insert-snippet "yasnippet" nil t)
(autoload #'yas-new-snippet "yasnippet" nil t)
(autoload #'yas-visit-snippet-file "yasnippet" nil t)
(autoload #'yas-load-directory "yasnippet" nil t)
(autoload #'yas-activate-extra-mode "yasnippet" nil t)
(autoload #'yas-tryout-snippet "yasnippet" nil t)
(autoload #'yas-describe-tables "yasnippet" nil t)
(autoload #'yas/global-mode "yasnippet" nil t)
(autoload #'yas/minor-mode "yasnippet" nil t)
(autoload #'yas-reload-all "yasnippet" nil t)
(autoload #'hydra-yas-primary/body "yasnippet" nil t)
(autoload #'hydra-yas/body "yasnippet" nil t)
(leaf-keys
((:yas-minor-mode-map :package yasnippet
("<f3>" . hydra-yas-primary/body)
("<f2>" . hydra-yas/body))))
(eval-after-load 'hydra
'(progn
(defhydra hydra-yas-primary
(:hint nil)
"yas-primary"
("i" yas-insert-snippet)
("n" yas-new-snippet)
("v" yas-visit-snippet-file))
(defhydra hydra-yas
(:color blue :hint nil)
"
^YASnippets^
--------------------------------------------
Modes: Load/Visit: Actions:
_g_lobal _d_irectory _i_nsert
_m_inor _f_ile _t_ryout
_e_xtra _l_ist _n_ew
_a_ll
"
("d" yas-load-directory)
("e" yas-activate-extra-mode)
("i" yas-insert-snippet)
("f" yas-visit-snippet-file :color blue)
("n" yas-new-snippet)
("t" yas-tryout-snippet)
("l" yas-describe-tables)
("g" yas/global-mode)
("m" yas/minor-mode)
("a" yas-reload-all))))))))
And leaf support major-mode-hydra and pretty-hydra via :mode-hydra
and :pretty-hydra
.
(cort-deftest-with-macroexpand leaf/mode-hydra
'(
;; assume leaf--name as major-mode and no body
((leaf go-mode
:ensure t
:mode "\\.go\\'"
:mode-hydra
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))
(prog1 'go-mode
(unless (fboundp 'godoc-at-point) (autoload #'godoc-at-point "go-mode" nil t))
(unless (fboundp 'go-import-add) (autoload #'go-import-add "go-mode" nil t))
(unless (fboundp 'go-remove-unused-imports) (autoload #'go-remove-unused-imports "go-mode" nil t))
(unless (fboundp 'go-mode) (autoload #'go-mode "go-mode" nil t))
(declare-function godoc-at-point "go-mode")
(declare-function go-import-add "go-mode")
(declare-function go-remove-unused-imports "go-mode")
(declare-function go-mode "go-mode")
(leaf-handler-package go-mode go-mode nil)
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
(major-mode-hydra-define+ go-mode nil
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))))
;; assume leaf--name as major-mode and spesific body
((leaf go-mode
:ensure t
:mode "\\.go\\'"
:mode-hydra
((:title "Go Commands")
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup")))))
(prog1 'go-mode
(unless (fboundp 'godoc-at-point) (autoload #'godoc-at-point "go-mode" nil t))
(unless (fboundp 'go-import-add) (autoload #'go-import-add "go-mode" nil t))
(unless (fboundp 'go-remove-unused-imports) (autoload #'go-remove-unused-imports "go-mode" nil t))
(unless (fboundp 'go-mode) (autoload #'go-mode "go-mode" nil t))
(declare-function godoc-at-point "go-mode")
(declare-function go-import-add "go-mode")
(declare-function go-remove-unused-imports "go-mode")
(declare-function go-mode "go-mode")
(leaf-handler-package go-mode go-mode nil)
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
(major-mode-hydra-define+ go-mode
(:title "Go Commands")
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))))
;; specify major-mode and body
((leaf go-mode
:ensure t
:mode "\\.go\\'"
:mode-hydra
(go-mode
(:title "Go Commands")
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup")))))
(prog1 'go-mode
(unless (fboundp 'godoc-at-point) (autoload #'godoc-at-point "go-mode" nil t))
(unless (fboundp 'go-import-add) (autoload #'go-import-add "go-mode" nil t))
(unless (fboundp 'go-remove-unused-imports) (autoload #'go-remove-unused-imports "go-mode" nil t))
(unless (fboundp 'go-mode) (autoload #'go-mode "go-mode" nil t))
(declare-function godoc-at-point "go-mode")
(declare-function go-import-add "go-mode")
(declare-function go-remove-unused-imports "go-mode")
(declare-function go-mode "go-mode")
(leaf-handler-package go-mode go-mode nil)
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
(major-mode-hydra-define+ go-mode
(:title "Go Commands")
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))))
;; define two or more :mode-hydra at once
((leaf elisp-mode
:mode-hydra
(emacs-lisp-mode
(:title "Emacs-lisp Commands")
("Eval"
(("b" eval-buffer "buffer")
("e" eval-defun "defun")
("r" eval-region "region"))))
(lisp-interaction-mode
(:title "Lisp interaction Commands")
("Eval"
(("t" eval-print-last-sexp "this")))))
(prog1 'elisp-mode
(unless (fboundp 'eval-buffer) (autoload #'eval-buffer "elisp-mode" nil t))
(unless (fboundp 'eval-defun) (autoload #'eval-defun "elisp-mode" nil t))
(unless (fboundp 'eval-region) (autoload #'eval-region "elisp-mode" nil t))
(unless (fboundp 'eval-print-last-sexp) (autoload #'eval-print-last-sexp "elisp-mode" nil t))
(declare-function eval-buffer "elisp-mode")
(declare-function eval-defun "elisp-mode")
(declare-function eval-region "elisp-mode")
(declare-function eval-print-last-sexp "elisp-mode")
(major-mode-hydra-define+ emacs-lisp-mode
(:title "Emacs-lisp Commands")
("Eval"
(("b" eval-buffer "buffer")
("e" eval-defun "defun")
("r" eval-region "region"))))
(major-mode-hydra-define+ lisp-interaction-mode
(:title "Lisp interaction Commands")
("Eval"
(("t" eval-print-last-sexp "this"))))))))
(cort-deftest-with-macroexpand leaf/pretty-hydra
'(
;; assume leaf--name as major-mode and no body
((leaf go-mode
:ensure t
:mode "\\.go\\'"
:pretty-hydra
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))
(prog1 'go-mode
(unless (fboundp 'godoc-at-point) (autoload #'godoc-at-point "go-mode" nil t))
(unless (fboundp 'go-import-add) (autoload #'go-import-add "go-mode" nil t))
(unless (fboundp 'go-remove-unused-imports) (autoload #'go-remove-unused-imports "go-mode" nil t))
(unless (fboundp 'go-mode) (autoload #'go-mode "go-mode" nil t))
(declare-function godoc-at-point "go-mode")
(declare-function go-import-add "go-mode")
(declare-function go-remove-unused-imports "go-mode")
(declare-function go-mode "go-mode")
(leaf-handler-package go-mode go-mode nil)
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
(pretty-hydra-define+ go-mode nil
("Doc"
(("d" godoc-at-point "doc at point"))
"Imports"
(("ia" go-import-add "add")
("ir" go-remove-unused-imports "cleanup"))))))))
:transient
provide frontend for transient.
If you pass a list, you pass it to define-transient-command
, and if you pass a nested list, you pass each one to it.
(cort-deftest-with-macroexpand leaf/transient
'(((leaf dired-git
:transient
(transient-dwim-dired-mode--git
()
"Transient-dwim for `dired-mode--git'."
[["Worktree"
("c" "Commit" dired-git-commit)
("S" "Stage" dired-git-stage)
("U" "Unstage" dired-git-unstage)
("zz" "Stash" dired-git-stash)
("zp" "Stash pop" dired-git-stash-pop)
("X" "Reset --hard" dired-git-reset-hard)]
["Branch"
("b" "Branch" dired-git-branch)
("t" "Tag" dired-git-tag)
("f" "Fetch" dired-git-fetch)
("F" "Pull" dired-git-pull)
("m" "Merge" dired-git-merge)
("P" "Push" dired-git-push)
("!" "Run" dired-git-run)]]))
(prog1 'dired-git
(transient-define-prefix transient-dwim-dired-mode--git ()
"Transient-dwim for `dired-mode--git'."
[["Worktree"
("c" "Commit" dired-git-commit)
("S" "Stage" dired-git-stage)
("U" "Unstage" dired-git-unstage)
("zz" "Stash" dired-git-stash)
("zp" "Stash pop" dired-git-stash-pop)
("X" "Reset --hard" dired-git-reset-hard)]
["Branch"
("b" "Branch" dired-git-branch)
("t" "Tag" dired-git-tag)
("f" "Fetch" dired-git-fetch)
("F" "Pull" dired-git-pull)
("m" "Merge" dired-git-merge)
("P" "Push" dired-git-push)
("!" "Run" dired-git-run)]])))))
:chord
and :chord*
provide frontend for leaf-key-chord
which bind key for key-chord.
The usage and notes are the same as for the :bind
keyword.
(cort-deftest-with-macroexpand leaf/chord
'(((leaf macrostep
:ensure t
:chord (("jk" . macrostep-expand)))
(prog1 'macrostep
(autoload #'macrostep-expand "macrostep" nil t)
(leaf-handler-package macrostep macrostep nil)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . macrostep-expand)))))))
((leaf macrostep
:ensure t
:chord ("jk" . macrostep-expand))
(prog1 'macrostep
(autoload #'macrostep-expand "macrostep" nil t)
(leaf-handler-package macrostep macrostep nil)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . macrostep-expand)))))))
((leaf color-moccur
:chord
("jk" . moccur)
("fi" . isearch-moccur))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . moccur)
("fi" . isearch-moccur)))))))
((leaf color-moccur
:chord (("jk" . moccur)
("fi" . isearch-moccur)))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . moccur)
("fi" . isearch-moccur)))))))
((leaf color-moccur
:chord
("jk" . nil)
("fi" . isearch-moccur))
(prog1 'color-moccur
(autoload #'isearch-moccur "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk")
("fi" . isearch-moccur)))))))
((leaf color-moccur
:chord (("jk" . nil)
("fi" . isearch-moccur)))
(prog1 'color-moccur
(autoload #'isearch-moccur "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk")
("fi" . isearch-moccur)))))))
((leaf color-moccur
:chord
("jk" . moccur)
(:isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all)))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . moccur)
(:isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all))))))))
((leaf color-moccur
:chord (("jk" . moccur)
(:isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all))))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . moccur)
(:isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all))))))))
;; you also use symbol instead of keyword to specify keymap
((leaf color-moccur
:chord (("jk" . moccur)
(isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all))))
(prog1 'color-moccur
(autoload #'moccur "color-moccur" nil t)
(autoload #'isearch-moccur "color-moccur" nil t)
(autoload #'isearch-moccur-all "color-moccur" nil t)
(eval-after-load 'key-chord
'(progn
(leaf-key-chords
(("jk" . moccur)
(isearch-mode-map
:package isearch
("ji" . isearch-moccur)
("jo" . isearch-moccur-all))))))))))
(cort-deftest-with-macroexpand leaf/leaf-key-chord
'(((leaf-key-chord "jj" 'undo 'c-mode-map)
(leaf-key [key-chord 106 106] 'undo 'c-mode-map))
((leaf-key-chord "jk" 'undo 'c-mode-map)
(progn
(leaf-key [key-chord 106 107] 'undo 'c-mode-map)
(leaf-key [key-chord 107 106] 'undo 'c-mode-map)))
((leaf-key-chord "jj" 'undo)
(leaf-key [key-chord 106 106] 'undo nil))
((leaf-key-chord "jk" 'undo)
(progn
(leaf-key [key-chord 106 107] 'undo nil)
(leaf-key [key-chord 107 106] 'undo nil)))))
:smartrep
and :smartrep*
provide frontend for smartrep.
They can process a list of arguments that the smartrep
accepts, or a nested list of them.
Automatically generates an autoload
statement when a function symbol is passed.
Quoting a function or quoting a binding list works the same way.
If you omit the key-map to bind, use global-map
instead in :smartrep
and
leaf-key-override-global-map
for leaf-key in :smartrep*
.
(cort-deftest-with-macroexpand leaf/smartrep
'(((leaf multiple-cursors
:smartrep ("C-t"
(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this)
("u" . mc/unmark-next-like-this)
("U" . mc/unmark-previous-like-this)
("s" . mc/skip-to-next-like-this)
("S" . mc/skip-to-previous-like-this)
("*" . mc/mark-all-like-this))))
(prog1 'multiple-cursors
(autoload #'mc/mark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-next-like-this "multiple-cursors" nil t)
(autoload #'mc/unmark-next-like-this "multiple-cursors" nil t)
(autoload #'mc/unmark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/skip-to-next-like-this "multiple-cursors" nil t)
(autoload #'mc/skip-to-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-all-like-this "multiple-cursors" nil t)
(eval-after-load 'smartrep
'(progn
(smartrep-define-key global-map "C-t"
'(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this)
("u" . mc/unmark-next-like-this)
("U" . mc/unmark-previous-like-this)
("s" . mc/skip-to-next-like-this)
("S" . mc/skip-to-previous-like-this)
("*" . mc/mark-all-like-this)))))))
((leaf multiple-cursors
:smartrep (global-map
"C-t"
(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this))))
(prog1 'multiple-cursors
(autoload #'mc/mark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-next-like-this "multiple-cursors" nil t)
(eval-after-load 'smartrep
'(progn
(smartrep-define-key global-map "C-t"
'(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this)))))))
((leaf multiple-cursors
:smartrep (global-map
"C-t"
(("C-p" . 'mc/mark-previous-like-this)
("C-n" . 'mc/mark-next-like-this))))
(prog1 'multiple-cursors
(autoload #'mc/mark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-next-like-this "multiple-cursors" nil t)
(eval-after-load 'smartrep
'(progn
(smartrep-define-key global-map "C-t"
'(("C-p" quote mc/mark-previous-like-this)
("C-n" quote mc/mark-next-like-this)))))))
((leaf multiple-cursors
:smartrep (global-map
"C-t"
'(("C-p" . 'mc/mark-previous-like-this)
("C-n" . 'mc/mark-next-like-this))))
(prog1 'multiple-cursors
(autoload #'mc/mark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-next-like-this "multiple-cursors" nil t)
(eval-after-load 'smartrep
'(progn
(smartrep-define-key global-map "C-t"
'(("C-p" quote mc/mark-previous-like-this)
("C-n" quote mc/mark-next-like-this)))))))
((leaf org
:smartrep (org-mode-map
"C-c"
(("C-n" . (outline-next-visible-heading 1))
("C-p" . (outline-previous-visible-heading 1)))))
(prog1 'org
(eval-after-load 'smartrep
'(progn
(smartrep-define-key org-mode-map "C-c"
'(("C-n" outline-next-visible-heading 1)
("C-p" outline-previous-visible-heading 1)))))))
((leaf org
:smartrep ((org-mode-map
"C-c"
(("C-n" . (outline-next-visible-heading 1))
("C-p" . (outline-previous-visible-heading 1))))
("s-c"
(("M-n" . (outline-next-visible-heading 1))
("M-p" . (outline-previous-visible-heading 1))))))
(prog1 'org
(eval-after-load 'smartrep
'(progn
(smartrep-define-key org-mode-map "C-c"
'(("C-n" outline-next-visible-heading 1)
("C-p" outline-previous-visible-heading 1)))
(smartrep-define-key global-map "s-c"
'(("M-n" outline-next-visible-heading 1)
("M-p" outline-previous-visible-heading 1)))))))))
(cort-deftest-with-macroexpand leaf/smartrep*
'(((leaf multiple-cursors
:smartrep* ("C-t"
(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this)
("u" . mc/unmark-next-like-this)
("U" . mc/unmark-previous-like-this)
("s" . mc/skip-to-next-like-this)
("S" . mc/skip-to-previous-like-this)
("*" . mc/mark-all-like-this))))
(prog1 'multiple-cursors
(autoload #'mc/mark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-next-like-this "multiple-cursors" nil t)
(autoload #'mc/unmark-next-like-this "multiple-cursors" nil t)
(autoload #'mc/unmark-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/skip-to-next-like-this "multiple-cursors" nil t)
(autoload #'mc/skip-to-previous-like-this "multiple-cursors" nil t)
(autoload #'mc/mark-all-like-this "multiple-cursors" nil t)
(eval-after-load 'smartrep
'(progn
(smartrep-define-key leaf-key-override-global-map "C-t"
'(("C-p" . mc/mark-previous-like-this)
("C-n" . mc/mark-next-like-this)
("u" . mc/unmark-next-like-this)
("U" . mc/unmark-previous-like-this)
("s" . mc/skip-to-next-like-this)
("S" . mc/skip-to-previous-like-this)
("*" . mc/mark-all-like-this)))))))
((leaf org
:smartrep* ((org-mode-map
"C-c"
(("C-n" . (outline-next-visible-heading 1))
("C-p" . (outline-previous-visible-heading 1))))
("s-c"
(("M-n" . (outline-next-visible-heading 1))
("M-p" . (outline-previous-visible-heading 1))))))
(prog1 'org
(eval-after-load 'smartrep
'(progn
(smartrep-define-key org-mode-map "C-c"
'(("C-n" outline-next-visible-heading 1)
("C-p" outline-previous-visible-heading 1)))
(smartrep-define-key leaf-key-override-global-map "s-c"
'(("M-n" outline-next-visible-heading 1)
("M-p" outline-previous-visible-heading 1)))))))))
:combo
, :combo*
provide frontend for key-combo.
They can process a list of arguments, or a nested list of them.
Automatically generates an autoload
statement when a function symbol is passed.
If you omit the key-map to bind, use global-map
instead in :combo
and
leaf-key-override-global-map
for leaf-key in :combo*
.
(cort-deftest-with-macroexpand leaf/key-combo
'(((leaf key-combo
:combo (("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define global-map "=>" " => ")
(key-combo-define global-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define global-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))))))
((leaf key-combo
:combo (emacs-lisp-mode-map
("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define emacs-lisp-mode-map "=" '(" = " " == " " === "))
(key-combo-define emacs-lisp-mode-map "=>" " => ")
(key-combo-define emacs-lisp-mode-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define emacs-lisp-mode-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))))))
((leaf key-combo
:combo ((("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return)))
(emacs-lisp-mode-map
("." . ("." " . "))
("=" . ("= " "eq " "equal ")))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define global-map "=>" " => ")
(key-combo-define global-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define global-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))
(key-combo-define emacs-lisp-mode-map "." '("." " . "))
(key-combo-define emacs-lisp-mode-map "=" '("= " "eq " "equal "))))))))
(cort-deftest-with-macroexpand leaf/key-combo*
'(((leaf key-combo
:combo* (("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define leaf-key-override-global-map "=>" " => ")
(key-combo-define leaf-key-override-global-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define leaf-key-override-global-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))))))
((leaf key-combo
:combo* (emacs-lisp-mode-map
("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define emacs-lisp-mode-map "=" '(" = " " == " " === "))
(key-combo-define emacs-lisp-mode-map "=>" " => ")
(key-combo-define emacs-lisp-mode-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define emacs-lisp-mode-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))))))
((leaf key-combo
:combo* ((("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return)))
(emacs-lisp-mode-map
("." . ("." " . "))
("=" . ("= " "eq " "equal ")))))
(prog1 'key-combo
(autoload #'back-to-indentation "key-combo" nil t)
(autoload #'move-beginning-of-line "key-combo" nil t)
(autoload #'beginning-of-buffer "key-combo" nil t)
(autoload #'key-combo-return "key-combo" nil t)
(autoload #'move-end-of-line "key-combo" nil t)
(autoload #'end-of-buffer "key-combo" nil t)
(eval-after-load 'key-combo
'(progn
(key-combo-define leaf-key-override-global-map "=>" " => ")
(key-combo-define leaf-key-override-global-map "C-a" '(back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
(key-combo-define leaf-key-override-global-map "C-e" '(move-end-of-line end-of-buffer key-combo-return))
(key-combo-define emacs-lisp-mode-map "." '("." " . "))
(key-combo-define emacs-lisp-mode-map "=" '("= " "eq " "equal "))))))))
:mode-hook
provides a front end for setting hooks.
Functions registered in hooks are automatically declared function as names like leaf-keywords-mode-hook--cc-mode--cc-mode-hook
.
If you write multiple expressions like :config
, the hook name is guessed from leaf-name
and the function is registered to the guessed hook.
To specify the hook name explicitly, specify a hook symbol in car
and a list of S expressions in cdr
.
(cort-deftest-with-macroexpand leaf/mode-hook
'((;; you can place sexp(s) like :config
(leaf cc-mode
:mode-hook
(electric-pair-mode 1)
(delete-selection-mode 1))
(prog1 'cc-mode
(leaf-keywords-handler-mode-hook cc-mode cc-mode-hook
(electric-pair-mode 1)
(delete-selection-mode 1))))
(;; you can configure multiple mode hooks
(leaf cc-mode
:config
(setq-default c-basic-offset 8)
:mode-hook
(c-mode-common-hook . ((setq-local tab-width 8)))
(java-mode-hook . ((setq-local tab-width 4)
(setq-local c-basic-offset 4))))
(prog1 'cc-mode
(leaf-keywords-handler-mode-hook cc-mode c-mode-common-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode java-mode-hook
(setq-local tab-width 4)
(setq-local c-basic-offset 4))
(setq-default c-basic-offset 8)))
(;; you can apply same sexp to multiple mode hooks
(leaf cc-mode
:config
(setq-default c-basic-offset 8)
:mode-hook
(c-mode-common-hook emacs-lisp-mode-hook lisp-mode-hook . ((setq-local tab-width 8)))
(java-mode-hook . ((setq-local tab-width 4)
(setq-local c-basic-offset 4))))
(prog1 'cc-mode
(leaf-keywords-handler-mode-hook cc-mode c-mode-common-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode emacs-lisp-mode-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode lisp-mode-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode java-mode-hook
(setq-local tab-width 4)
(setq-local c-basic-offset 4))
(setq-default c-basic-offset 8)))
(;; you can mix abobe two specification method
(leaf cc-mode
:config
(setq-default c-basic-offset 8)
:mode-hook
(setq-local tab-width 8)
(java-mode-hook . ((setq-local tab-width 4)
(setq-local c-basic-offset 4))))
(prog1 'cc-mode
(leaf-keywords-handler-mode-hook cc-mode cc-mode-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode java-mode-hook
(setq-local tab-width 4)
(setq-local c-basic-offset 4))
(setq-default c-basic-offset 8)))
(;; multiple keyword specification is supported
(leaf cc-mode
:config
(setq-default c-basic-offset 8)
:mode-hook
(setq-local tab-width 8)
(c-mode-common-hook . ((setq-local tab-width 8)))
:mode-hook
(java-mode-hook . ((setq-local tab-width 4)
(setq-local c-basic-offset 4))))
(prog1 'cc-mode
(leaf-keywords-handler-mode-hook cc-mode cc-mode-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode c-mode-common-hook
(setq-local tab-width 8))
(leaf-keywords-handler-mode-hook cc-mode java-mode-hook
(setq-local tab-width 4)
(setq-local c-basic-offset 4))
(setq-default c-basic-offset 8)))
(;; leaf-keywords-handler-mode-hook expand like below
(leaf-keywords-handler-mode-hook cc-mode cc-mode-hook
(electric-pair-mode 1)
(delete-selection-mode 1))
(progn
(defun leaf-keywords-mode-hook--cc-mode--cc-mode-hook ()
"Function autogenerated by leaf-keywords in leaf-block `cc-mode' for hook `cc-mode-hook'."
(electric-pair-mode 1)
(delete-selection-mode 1))
(add-hook 'cc-mode-hook 'leaf-keywords-mode-hook--cc-mode--cc-mode-hook)))))
These keywords are buildin. Info is here.
(cort-deftest-with-macroexpand leaf/package
'(((leaf leaf
:package t
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-init)))
((leaf leaf
:package t leaf-browser
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf leaf nil)
(leaf-handler-package leaf leaf-browser nil)
(leaf-init)))
((leaf leaf
:package feather leaf-key leaf-browser
:config (leaf-init))
(prog1 'leaf
(leaf-handler-package leaf feather nil)
(leaf-handler-package leaf leaf-key nil)
(leaf-handler-package leaf leaf-browser nil)
(leaf-init)))))
(cort-deftest-with-macroexpand leaf/handler-package
'(((leaf macrostep :ensure t)
(prog1 'macrostep
(leaf-handler-package macrostep macrostep nil))
((leaf-handler-package macrostep macrostep nil)
(unless
(package-installed-p 'macrostep)
(condition-case err
(progn
(unless (assoc 'macrostep package-archive-contents)
(package-refresh-contents))
(package-install 'macrostep))
(error
(condition-case err
(progn
(package-refresh-contents)
(package-install 'macrostep))
(error
(leaf-error "In `macrostep' block, failed to :package of macrostep. Error msg: %s"
(error-message-string err)))))))))))
:el-get
provide frontend of el-get-bundle
.
If you specify t
, leaf assumes that you specified the name of the leaf-block.
Given a list, the arguments are passed as is to the el-get-bundle
.
(cort-deftest-with-macroexpand leaf/el-get
'(((leaf leaf
:init (leaf-pre-init)
:el-get t
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get leaf leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get t
:el-get leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get t leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle leaf)
(el-get-bundle leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get (zenburn-theme
:url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle zenburn-theme :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:el-get
(yaicomplete
:url "https://github.com/tarao/elisp.git"
:features yaicomplete)
(zenburn-theme
:url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
(kazu-yamamoto/Mew :name mew :build ("./configure" "make"))
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'el-get
'(progn
(el-get-bundle yaicomplete :url "https://github.com/tarao/elisp.git" :features yaicomplete)
(el-get-bundle zenburn-theme :url "https://raw.githubusercontent.com/bbatsov/zenburn-emacs/master/zenburn-theme.el"
(load-theme 'zenburn t))
(el-get-bundle kazu-yamamoto/Mew :name mew :build ("./configure" "make"))))
(leaf-pre-init)
(leaf-init)))))
:straight
provides a frontend for straight-use-package
.
If you specify t
, leaf assumes that you specified the name of the leaf-block.
Given a list, the arguments are passed as is to straight-use-package
.
(cort-deftest-with-macroexpand leaf/straight
'(((leaf leaf
:init (leaf-pre-init)
:straight t
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight leaf leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight t
:straight leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight t leaf-polyfill
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package 'leaf)
(straight-use-package 'leaf-polyfill)))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight (zenburn-theme :type git :host github :repo "fake/fake")
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package '(zenburn-theme :type git :host github :repo "fake/fake"))))
(leaf-pre-init)
(leaf-init)))
((leaf leaf
:init (leaf-pre-init)
:straight
(zenburn-theme :type git :host github :repo "fake/fake")
(yaicomplete :type git :host github :repo "fake/faker")
(mew :type git :host gitlab :repo "fake/fakest" :no-build)
:config (leaf-init))
(prog1 'leaf
(eval-after-load 'straight
'(progn
(straight-use-package '(zenburn-theme :type git :host github :repo "fake/fake"))
(straight-use-package '(yaicomplete :type git :host github :repo "fake/faker"))
(straight-use-package '(mew :type git :host gitlab :repo "fake/fakest" :no-build))))
(leaf-pre-init)
(leaf-init)))))
:blackout
keyword provide frontend for blackout.
There are three packages that change the display of the modeline:
deminish
, delight
and blackout
, but the most recent
developed is blackout
, and the developer of leaf
recommend
that you use blackout
.
This package allows you to change the display of the major mode
as well as the minor mode. And it also requires two arguments,
so it can be set in the cons-cell and has a high affinity with
other keywords in leaf
.
(cort-deftest-with-macroexpand leaf/blackout
'(
;; t will be converted leaf--name
((leaf foo-mode
:blackout t)
(prog1 'foo-mode
(with-eval-after-load 'foo-mode
(blackout 'foo-mode nil))))
;; guess leaf--name as mode-name
((leaf foo
:blackout t)
(prog1 'foo
(with-eval-after-load 'foo
(blackout 'foo-mode nil))))
;; blackout if specify symbol only
((leaf simple
:blackout auto-fill-mode)
(prog1 'simple
(with-eval-after-load 'simple
(blackout 'auto-fill-mode nil))))
;; expect cons-cell to change display of a mode
((leaf simple
:blackout (auto-fill-mode . " Auto-Fill"))
(prog1 'simple
(with-eval-after-load 'simple
(blackout 'auto-fill-mode " Auto-Fill"))))
;; change major-mode display by same way
((leaf elisp-mode
:blackout (emacs-lisp-mode . "Elisp"))
(prog1 'elisp-mode
(with-eval-after-load 'elisp-mode
(blackout 'emacs-lisp-mode "Elisp"))))
;; cons-cell list also accepted
((leaf simple
:blackout ((auto-fill-mode . " Auto-Fill")
(overwrite-mode . " Overwrite")))
(prog1 'simple
(with-eval-after-load 'simple
(blackout 'auto-fill-mode " Auto-Fill")
(blackout 'overwrite-mode " Overwrite"))))
;; multi cons-cell also accepted
((leaf simple
:blackout
(auto-fill-mode . " Auto-Fill")
(overwrite-mode . " Overwrite"))
(prog1 'simple
(with-eval-after-load 'simple
(blackout 'auto-fill-mode " Auto-Fill")
(blackout 'overwrite-mode " Overwrite"))))
;; multi keyword also accepted
((leaf simple
:blackout (auto-fill-mode . " Auto-Fill")
:blackout (overwrite-mode . " Overwrite"))
(prog1 'simple
(with-eval-after-load 'simple
(blackout 'auto-fill-mode " Auto-Fill")
(blackout 'overwrite-mode " Overwrite"))))))
:diminish
keyword provide frontend for diminish.
(cort-deftest-with-macroexpand leaf/diminish
'(((leaf autorevert
:diminish t)
(prog1 'autorevert
(with-eval-after-load 'autorevert
(diminish 'autorevert-mode nil))))
((leaf autorevert
:diminish autorevert-mode)
(prog1 'autorevert
(with-eval-after-load 'autorevert
(diminish 'autorevert-mode nil))))
((leaf autorevert
:diminish t
:diminish autorevert-polyfill)
(prog1 'autorevert
(with-eval-after-load 'autorevert
(diminish 'autorevert-mode nil)
(diminish 'autorevert-polyfill-mode nil))))
((leaf autorevert
:diminish t autorevert-polyfill)
(prog1 'autorevert
(with-eval-after-load 'autorevert
(diminish 'autorevert-mode nil)
(diminish 'autorevert-polyfill-mode nil))))
((leaf go-mode
:diminish " Go")
(prog1 'go-mode
(with-eval-after-load 'go-mode
(diminish 'go-mode " Go"))))
((leaf abbrev
:diminish (abbrev-mode . " Abv"))
(prog1 'abbrev
(with-eval-after-load 'abbrev
(diminish 'abbrev-mode " Abv"))))
((leaf projectile
:diminish (projectile-mode . '(:eval (concat " " (projectile-project-name)))))
(prog1 'projectile
(with-eval-after-load 'projectile
(diminish 'projectile-mode
'(:eval (concat " " (projectile-project-name)))))))))
:delight
keyword provide frontend for delight (ELPA, Emacs wiki).
(cort-deftest-with-macroexpand leaf/delight
'(((leaf autorevert
:delight t)
(prog1 'autorevert
(delight 'autorevert-mode)))
((leaf autorevert
:delight autorevert)
(prog1 'autorevert
(delight 'autorevert-mode)))
((leaf autorevert
:delight t
:delight autorevert-polyfill)
(prog1 'autorevert
(delight 'autorevert-mode)
(delight 'autorevert-polyfill-mode)))
((leaf autorevert
:delight t autorevert-polyfill)
(prog1 'autorevert
(delight 'autorevert-mode)
(delight 'autorevert-polyfill-mode)))
((leaf go-mode
:delight " Go")
(prog1 'go-mode
(delight 'go-mode " Go")))
((leaf abbrev
:delight (abbrev-mode " Abv"))
(prog1 'abbrev
(delight 'abbrev-mode " Abv")))
((leaf projectile
:delight (projectile-mode '(:eval (concat " " (projectile-project-name)))))
(prog1 'projectile
(delight 'projectile-mode
'(:eval
(concat " "
(projectile-project-name))))))
((leaf delight
:delight ((abbrev-mode " Abv" "abbrev")
(smart-tab-mode " \\t" "smart-tab")
(eldoc-mode nil "eldoc")
(rainbow-mode)
(overwrite-mode " Ov" t)
(emacs-lisp-mode "Elisp" :major)))
(prog1 'delight
(delight 'abbrev-mode " Abv" "abbrev")
(delight 'smart-tab-mode " \\t" "smart-tab")
(delight 'eldoc-mode nil "eldoc")
(delight 'rainbow-mode)
(delight 'overwrite-mode " Ov" t)
(delight 'emacs-lisp-mode "Elisp" :major)))))
:grugru
keyword provide frontend for grugru.
grugru
allows you to define conversion rules for symbols based on
the major-mode
. This :grugru
keyword defines a conversion rule
for grugru
and, if you omit the measure mode specification, it
assumes that leaf--name
is the target major-mode
.
If leaf--name
is not suffixed with -mode
, it is automatically
compensated for. If your intended major-mode
does not follow
these rules, you cannot omit target major-mode
.
(e.g. c-mode
in cc-mode
)
(cort-deftest-with-macroexpand leaf/grugru
'(
;; grugru difinition with :grugru keyword
((leaf cc-mode
:grugru
(c-mode
(symbol "true" "false")))
(prog1 'cc-mode
(grugru-define-multiple
(c-mode (symbol "true" "false")))))
;; definition list also accepted
((leaf cc-mode
:grugru
((c-mode
(symbol "true" "false"))))
(prog1 'cc-mode
(grugru-define-multiple
(c-mode (symbol "true" "false")))))
;; grugru definition with major-mode list
((leaf cc-mode
:grugru
((c-mode c++-mode)
(symbol "true" "false")))
(prog1 'cc-mode
(grugru-define-multiple
((c-mode c++-mode)
(symbol "true" "false")))))
;; definition list with major-mode list
((leaf cc-mode
:grugru
(((c-mode c++-mode)
(symbol "true" "false"))))
(prog1 'cc-mode
(grugru-define-multiple
((c-mode c++-mode) (symbol "true" "false")))))
;; simple listed definition are inferred to be for leaf--name
((leaf lisp-mode
:grugru
(symbol "nil" "t")
(emacs-lisp-mode
(word "add" "remove")))
(prog1 'lisp-mode
(grugru-define-multiple
(lisp-mode (symbol "nil" "t"))
(emacs-lisp-mode (word "add" "remove")))))
;; simple listed definition list are inferred to be for leaf--name
((leaf lisp-mode
:grugru
((symbol "nil" "t")
(emacs-lisp-mode
(word "add" "remove"))))
(prog1 'lisp-mode
(grugru-define-multiple
(lisp-mode (symbol "nil" "t"))
(emacs-lisp-mode (word "add" "remove")))))
;; assume major-mode name from leaf--name
((leaf gnuplot
:grugru
((symbol "sin" "cos" "tan")
(symbol "log" "log10")))
(prog1 'gnuplot
(grugru-define-multiple
(gnuplot-mode
(symbol "sin" "cos" "tan"))
(gnuplot-mode
(symbol "log" "log10")))))))
This is a keyword that calls a dynamically named function.
If you define various settings for this function in a separate file, you may be able to reduce the number of lines in init.el. This is a one of use case of this.
Also, if you distribute the functions to be used, someone can use your recommended settings.
(cort-deftest-with-macroexpand leaf/defaults
'(((leaf helm
:ensure t
:defaults t)
(prog1 'helm
(leaf-handler-package helm helm nil)
(leaf-keywords-defaults--leaf/helm)))
((leaf helm
:when nil
:ensure t
:defaults t)
(prog1 'helm
(when nil
(leaf-handler-package helm helm nil)
(leaf-keywords-defaults--leaf/helm))))
((leaf helm
:ensure t
:defaults conao3)
(prog1 'helm
(leaf-handler-package helm helm nil)
(leaf-keywords-defaults--conao3/helm)))
((leaf helm
:ensure t
:defaults conao3 garario3)
(prog1 'helm
(leaf-handler-package helm helm nil)
(leaf-keywords-defaults--conao3/helm)
(leaf-keywords-defaults--garario3/helm)))
((leaf helm
:ensure t
:defaults conao3
:defaults garario3)
(prog1 'helm
(leaf-handler-package helm helm nil)
(leaf-keywords-defaults--conao3/helm)
(leaf-keywords-defaults--garario3/helm)))
((leaf helm
:ensure t
:defaults nil
:defaults conao3
:defaults garario3)
(prog1 'helm
(leaf-handler-package helm helm nil)))))
I love OSS and I am dreaming of working on it as full-time job.
With your support, I will be able to spend more time at OSS!
All feedback and suggestions are welcome!
You can use github issues, but you can also use Slack if you want a more casual conversation.
We welcome PR!
Travis Cl test leaf-test.el
with all Emacs version 24.4 or above.
I think that it is difficult to prepare the environment locally, so I think that it is good to throw PR and test Travis for the time being! Feel free throw PR!
The following script is useful for adding keywords. This is a simplified leaf
macro for *scratch*
.
You first design the list that the normalizer should return and define the keyword processor.
Then trial-and-error builds the normalizer by this script, and by typing C-M-x (eval-defun)
at the beginning of defcustom
,
it can be overwrite variable and recognized by leaf
(At that time the function to specify :set
is executed.).
Once you have the S-expression expected from macrostep, let leaf-keywords-test.el
define multiple tests
to ensure that they will execute correctly into the future.
(let ((name 'leaf)
(args '(;; << Your new leaf argument >>
:combo (("=" . (" = " " == " " === " ))
("=>" . " => ")
("C-a" . (back-to-indentation move-beginning-of-line beginning-of-buffer key-combo-return))
("C-e" . (move-end-of-line end-of-buffer key-combo-return))))))
;; call `leaf'
(let* ((leaf--autoload)
;; omit `leaf-append-defaults' to debug
(args* (leaf-sort-values-plist
(leaf-normalize-plist args 'merge 'eval))))
;; call `leaf-process-keywords'
(let ((name name) (plist args*) (raw args*))
(let* ((leaf--name name)
(leaf--key (pop plist))
(leaf--keyname (substring (symbol-name leaf--key) 1))
(leaf--value (pop plist))
(leaf--raw raw)
(leaf--rest plist)
(leaf--body))
;; renew (normalize) leaf--value, save follow expansion in leaf--body
(setq leaf--value
(cond
;; << Your new normalizer >>
((memq leaf--key '(:combo :combo*))
(let ((map (if (eq :combo leaf--key) 'global-map 'leaf-key-override-global-map))
(val) (fns))
(setq val (mapcan
(lambda (elm)
(cond
((and (listp elm)
(listp (car elm))
(listp (caar elm)))
(mapcan
(lambda (el)
(let ((emap (and (symbolp (car el)) (car el))) ; el's map
(binds (if (leaf-pairp (car el)) el (cdr el))))
(mapcar
(lambda (el)
(setq fns (append fns (if (listp (cdr el)) (cdr el) `(,(cdr el)))))
`(,(or emap map) ,(car el) ,(if (stringp (cdr el)) (cdr el) `',(cdr el))))
binds)))
elm))
((listp elm)
(let ((emap (and (symbolp (car elm)) (car elm))) ; elm's map
(binds (if (leaf-pairp (car elm)) elm (cdr elm))))
(mapcar
(lambda (el)
(setq fns (append fns (if (listp (cdr el)) (cdr el) `(,(cdr el)))))
`(,(or emap map) ,(car el) ,(if (stringp (cdr el)) (cdr el) `',(cdr el))))
binds)))))
leaf--value))
`(,val ,(delq nil (mapcar (lambda (elm) (when (symbolp elm) elm)) fns)))))))
(pp `((:dummy)
========== leaf--value
,leaf--value
(:dummy)
========== leaf--body
(progn
,@(eval (plist-get leaf-keywords leaf--key)))
))
nil))))
Note: macrostep
return function
instead of #’, replace it via follow regexp by C-M-% (query-replace-regexp)
.
(autoload (function \([^ ]*\)) \([^ ]*\) → (autoload #’\1 \2
We also have leaf-keywords-after-require
, it’s confusing.
Plsease use leaf-keywords-after-require
.
We also have leaf-keyowrds-after-require
,
we should use leaf-keyowrds-before-require
to consistency.
General Public License Version 3 (GPLv3) Copyright (c) Naoya Yamashita - https://conao3.com https://github.com/conao3/leaf-keywords.el/blob/master/LICENSE
- Naoya Yamashita (conao3)
Advice and comments given by Emacs-JP’s forum member has been a great help
in developing leaf-keywords.el
.
Thank you very much!!