An open API service indexing awesome lists of open source software.

https://github.com/rocktakey/mic

Minimal and combinable configuration manager for Emacs
https://github.com/rocktakey/mic

Last synced: 3 months ago
JSON representation

Minimal and combinable configuration manager for Emacs

Awesome Lists containing this project

README

        

[[https://github.com/ROCKTAKEY/mic][https://img.shields.io/github/tag/ROCKTAKEY/mic.svg?style=flat-square]]
[[file:LICENSE][https://img.shields.io/github/license/ROCKTAKEY/mic.svg?style=flat-square]]
[[https://codecov.io/gh/ROCKTAKEY/mic?branch=master][https://img.shields.io/codecov/c/github/ROCKTAKEY/mic.svg?style=flat-square]]
[[https://github.com/ROCKTAKEY/mic/actions][https://img.shields.io/github/actions/workflow/status/ROCKTAKEY/mic/test.yml.svg?branch=master&style=flat-square]]
* mic: Minimal and combinable configuration manager for Emacs
#+BEGIN_QUOTE
=mic= is uncustomizable. Define your own =mic=.
#+END_QUOTE

=mic= is minimal and combinable configuration manager for Emacs.
This package is yet another =use-package= and =leaf=, but is also used with them (See [[#alternative][Alternative]]).
=mic= is minimal, so if you would like to write complex configuration,
=mic= is a little redundant. However, there is no problem. =mic= is combinable, in the other words, thought to be
used as core to define your own, and more convenient =mic=.
There are some functions to define your own =mic=. See [[#define-your-own-mic][Define your own mic]].

* How to Use?
For Emacs Lisp beginners, original =mic= macro is useful to configure your =init.el=.
#+begin_src emacs-lisp :tangle yes
(mic lsp-mode
;; These are transformed to `define-key' sexp.
;; Each argument is `(KEYMAP (KEYS . COMMAND)...)'.
;; KEYS is passed to `kbd'.
:define-key
((global-map
("M-l" . #'lsp)))

;; These are same as `:define-key' argument,
;; but evaluated after loading the feature (`lsp-mode' for this example).
;; This is needed because `lsp-mode-map' is unavailable before `lsp'
;; loading.
:define-key-after-load
((lsp-mode-map
("M-r" . #'lsp-rename)
("M-c" . #'lsp-execute-code-action)))

;; These are transformed to `with-eval-after-load' and `define-key' sexp.
;; Each argument is `(FEATURE (KEYMAP (KEYS . COMMAND)...))'.
;; `cdr' is same as `:define-key' arguments. Each `define-key' sexp is
;; evaluated after FEATURE is loaded.
;; This is needed because `dired-mode-map' is unavailable before `dired'
;; loading.
:define-key-with-feature
((dired
(dired-mode-map
("M-q" . #'lsp-dired-mode))))

;; These are transformed to `customize-set-variable' sexp.
;; Each argument is `(VARIABLE . VALUE)'.
:custom
((lsp-sesstion-file . (expand-file-name "etc/.lsp-session-v1" user-emacs-directory))
(lsp-log-io . t))

;; These are transformed to `add-hook' sexp.
;; Each argument is `(HOOK . FUNCTION)'.
:hook
((c-mode-hook . #'lsp)
(c++-mode-hook . #'lsp)
(tex-mode-hook . #'lsp)
(latex-mode-hook . #'lsp)
(bibtex-mode-hook . #'lsp)
(rust-mode-hook . #'lsp))

;; Each element is evaluated immediately when this `mic' sexp is evaluated.
:eval
((message "This is evaluated when this `mic' sexp is evaluated.")
(message "This is also evaluated."))

;; Each element will be evaluated after the package (`lsp-mode' for this example) is loaded.
:eval-after-load
((message "This is evaluated when `lsp-mode' is loaded."))

;; Each element is evaluated immediately when this `mic' sexp is evaluated.
;; These are evaluated before `:eval' and `:eval-after-load' elements.
;; This is for such use as defining function to use `:custom' argument.
:eval-before-all
((message "This is evaluated when this `mic' sexp is evaluated.")
(message "These are evaluated before `:eval' and `:eval-after-load' sexp.")))

;; `mic' sexp above is expanded to:
(prog1 'lsp-mode
;; `:eval-before-all'
(message "This is evaluated when this `mic' sexp is evaluated.")
(message "These are evaluated before `:eval' and `:eval-after-load' sexp.")

;; `:eval-after-load'
(with-eval-after-load 'lsp-mode
(message "This is evaluated when `lsp-mode' is loaded.")
;; `:define-key-after-load'
(define-key lsp-mode-map
(kbd "M-r")
(function lsp-rename))
(define-key lsp-mode-map
(kbd "M-c")
(function lsp-execute-code-action)))

;; `:eval'
(message "This is evaluated when this `mic' sexp is evaluated.")
(message "This is also evaluated.")

;; `:custom'
(customize-set-variable 'lsp-sesstion-file
(expand-file-name "etc/.lsp-session-v1" user-emacs-directory))
(customize-set-variable 'lsp-log-io t)

;; `:define-key'
(define-key global-map (kbd "M-l") #'lsp)

;; `:define-key-with-feature'
(with-eval-after-load 'dired
(define-key dired-mode-map (kbd "M-q") #'lsp-dired-mode))

;; `:hook'
(add-hook 'c-mode-hook #'lsp)
(add-hook 'c++-mode-hook #'lsp)
(add-hook 'tex-mode-hook #'lsp)
(add-hook 'latex-mode-hook #'lsp)
(add-hook 'bibtex-mode-hook #'lsp)
(add-hook 'rust-mode-hook #'lsp))
#+end_src

For Emacs Lisp expert, original =mic= is a little unsatisfactory or redundant.
=mic= is not customizable, but you can define your own =mic= easily.
1. Determine parent. You can use as parent =mic=, =mic-core=, which is simpler =mic=.
=mic-core= recieves only keywords start from =:eval=, such as =:eval=, =eval-after-load=.
2. Define filter functions. Each one recieves plist (property list) and returns plist.
returned plist is passed to parent (such as =mic=, =mic-core=) or next filter.
Note that filter function can get feature name as value of property =:name=.
Of course, you can use pre-defined filters. =mic= is defined by some filters
from the parent =mic-core=.
3. Define your own mic by =mic-defmic=. It recieves =NAME=, optional =DOCSTRING=,
and keyword argument =FILTERS=. =NAME= is name of your own =mic=.
=DOCSTRING= is the document string of yours. =FILTERS= are list of filter.
As explained, filter recieves plist and returns plist. It filter plist to get
desired behavior.

#+begin_src emacs-lisp :tangle yes
(defun my-filter-global-set-key-without-quote (plist)
(let ((alist
;; Get value from your own keyword
(plist-get plist :bind))
sexps)
(setq sexps
;; Transform each element
(mapcar
(lambda (arg)
(let ((keys (car arg))
(command (cdr arg)))
`(global-set-key (kbd ,keys) #',command)))
alist))
;; Put sexps to `:eval' arguments
(mic-plist-put-append plist :eval sexps)
;; Don't forget to delete your own keyword!
;; When forget it, parent recieves it and may cause unexpected result.
(mic-plist-delete plist :bind)
plist))

(mic-defmic mymic
;; Parent is here. You can also use `mic-core'.
mic
:filters
'(my-filter-global-set-key-without-quote
;; You can add other filters below
))

;; Then you can use `mymic' like:
(mymic simple
:bind
(("C-d" . delete-forward-char)
("C-x l" . toggle-truncate-lines))
;; Of course parent keywords are accepted.
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50)))

;; `mymic' sexp is expanded to:
(mic simple
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50))
:eval
((global-set-key (kbd "C-d") #'delete-forward-char)
(global-set-key (kbd "C-x l") #'toggle-truncate-lines)))

;; Expanded to:
(mic-core simple
:eval
((global-set-key (kbd "C-d") #'delete-forward-char)
(global-set-key (kbd "C-x l") #'toggle-truncate-lines)
(customize-set-variable 'kill-whole-line t)
(customize-set-variable 'set-mark-command-repeat-pop t)
(customize-set-variable 'mark-ring-max 50))
:eval-after-load nil)

;; Expanded to:
(prog1 'simple
(global-set-key (kbd "C-d") #'delete-forward-char)
(global-set-key (kbd "C-x l") #'toggle-truncate-lines)
(customize-set-variable 'kill-whole-line t)
(customize-set-variable 'set-mark-command-repeat-pop t)
(customize-set-variable 'mark-ring-max 50))
#+end_src

* Use =mic-core=, minimum one
:PROPERTIES:
:CUSTOM_ID: mic-core
:END:
=mic-core= is minimum. It can recieves only several keywords:
- =:eval=
- =:eval-after-load=
- =:eval-after-others=
- =:eval-after-others-after-load=
- =:eval-before-all=
- =:eval-installation=

Each element of =:eval= arguments are evaluated.
Time to evaluate is different.

** =:eval=, =:eval-after-others=, =:eval-before-all=
Each element of these arguments are evaluated when the =mic= sexp is evaluated.
The order is:
- =:eval-before-all=
- (=with-eval-after-load= sexp, explained on [[#eval-after-load][=eval-after-load= keyword section]], is evaluated)
- =:eval=
- =:eval-after-others=

#+begin_src emacs-lisp :tangle yes
(mic-core feature-name
:eval
((message "eval1")
(message "eval2"))
:eval-after-others
((message "eval-after-others1")
(message "eval-after-others2"))
:eval-before-all
((message "eval-before-all1")
(message "eval-before-all2"))
:eval-after-load
((message "eval-after-load1")
(message "eval-after-load2")))

;; Expanded to:
(prog1 'feature-name
(message "eval-before-all1")
(message "eval-before-all2")
(with-eval-after-load 'feature-name
(message "eval-after-load1")
(message "eval-after-load2"))
(message "eval1")
(message "eval2")
(message "eval-after-others1")
(message "eval-after-others2"))
#+end_src

=:eval-before-all= exists because a filter function appends sexp to =:eval= argument.
When some action should be evaluated before all action added by other filters,
you can put it to =:eval-before-all= argument. *Note that it should NOT be used
by filters.* Any filter should not use this. If it is used by filters,
users cannot make their sexp to be evaluate before filter sexps.

=:eval-after-others= exists because similar reason to =:eval-before-all=.
Some action should be evaluated after all action added by other filters.
Because of same reasons as =:eval-before-all=, *it should NOT be used
by filters*.

** =:eval-after-load=, =:eval-after-others-after-load=
:PROPERTIES:
:CUSTOM_ID: eval-after-load
:END:
Each element of these arguments are evaluated after the package is loaded.
The evaluated order is:
- =:eval-after-load=
- =:eval-after-others-after-load=

#+begin_src emacs-lisp :tangle yes
(mic-core feature-name
:eval-after-load
((message "eval-after-load1")
(message "eval-after-load2"))
:eval-after-others-after-load
((message "eval-after-others-after-load1")
(message "eval-after-others-after-load2")))

;; Expanded to:
(prog1 'feature-name
(with-eval-after-load 'feature-name
(message "eval-after-load1")
(message "eval-after-load2")
(message "eval-aftepr-others-after-load1")
(message "eval-after-others-after-load2")))
#+end_src

=:eval-after-others-after-load= exists because similar reason to =:eval-after-others=.
Some action should be evaluated after all action added by other filters.
Because of same reasons as =:eval-before-all=, *it should NOT be used
by filters*.

** =:eval-installation=
:PROPERTIES:
:CUSTOM_ID: eval-installation
:END:
Each element of this argument is evaluated before evaluation of other =:eval*= argument except =:eval-before-all=.
This exists because sexp to install the package is evaluated before sexp which uses package features.

#+begin_src emacs-lisp :tangle yes
(mic-core feature-name
:eval-before-all
((message "before all2")
(message "before all1"))
:eval-installation
((message "install1")
(message "install2"))
:eval-after-load
((message "eval-after-load1")
(message "eval-after-load2"))
:eval-after-others-after-load
((message "eval-after-others-after-load1")
(message "eval-after-others-after-load2"))
:eval
((message "eval1")
(message "eval2")))

;; Expanded to:
(prog1 'feature-name
(message "before all2")
(message "before all1")
(message "install1")
(message "install2")
(with-eval-after-load 'feature-name
(message "eval-after-load1")
(message "eval-after-load2")
(message "eval-after-others-after-load1")
(message "eval-after-others-after-load2"))
(message "eval1")
(message "eval2"))
#+end_src

=:eval-after-others-after-load= exists because similar reason to =:eval-after-others=.
Some action should be evaluated after all action added by other filters.
Because of same reasons as =:eval-before-all=, *it should NOT be used
by filters*.

* Use default =mic=
=mic= is minimal for use. =mic-core= is minimum core, but it is not enough to use as it is.
In addition to keywords allowed by [[#mic-core][=mic-core=]], it allows some keyword arguments:
- =:autoload-interactive=
- =:autoload-noninteractive=
- =:auto-mode=
- =:custom=
- =:custom-after-load=
- =:declare-function=
- =:define-key=
- =:define-key-after-load=
- =:define-key-with-feature=
- =:defvar-noninitial=
- =:face=
- =:hook=
- =:package=
- =:require=
- =:require-after=

** =:autoload-interactive=, =:autoload-noninteractive=

These are transformed to =autoload= sexps. Each element is function to autoload.
Since =autoload= should be informed whether the function is =interactive= or not,
both =:autoload-interactive= and =:autoload-noninteractive= exist.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:autoload-interactive
(interactive-func1
interactive-func2)
:autoload-noninteractive
(noninteractive-func3
noninteractive-func4))

;; Expanded to:
(mic-core feature-name :eval
((autoload #'interactive-func1 "feature-name" nil t)
(autoload #'interactive-func2 "feature-name" nil t)
(autoload #'noninteractive-func3 "feature-name")
(autoload #'noninteractive-func4 "feature-name"))
:eval-after-load nil)

;; Expanded to:
(prog1 'feature-name
(autoload #'interactive-func1 "feature-name" nil t)
(autoload #'interactive-func2 "feature-name" nil t)
(autoload #'noninteractive-func3 "feature-name")
(autoload #'noninteractive-func4 "feature-name"))
#+end_src

** =:auto-mode=
It is transformed to sexp like =(add-to-list 'auto-mode-alist ...)=.
Each element of the value should be valid as an element of =auto-mode-alist=.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:auto-mode
(("\\.html?\\'" . web-mode)
("\\.css\\'" . web-mode)))

;; Expanded to:
(mic-core feature-name :eval-installation
((add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode)))
:eval nil :eval-after-load nil)

;; Expanded to:
(prog1 'feature-name
(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode)))

#+end_src

** =:custom=, =:custom-after-load=
These are transformed to =customize-set-variable= sexps.
Each element is =(VARIABLE . VALUE)=.
Each =VARIABLE= is set to =VALUE=.
Sexp from =:custom= argument are evaluated when the =mic= sexp is evaluated,
while sexp from =:custom-after-load= argument are evaluated after the feature is loaded.
=:custom-after-load= is used when you want to use initial value of customized variable
or function defined in the feature.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:custom
((variable1 . 1)
;; VALUE is evaluated
(variable2 . (+ 1 1)))
:custom-after-load
;; You can use the initial value of `variable3'
((variable3 . (+ variable3 1))
;; You can use function defined in the feature (for this example `feature-name')
(variable2 . (function-defined-in-feature-name))))

;; Expanded to:
(mic-core feature-name
:eval
((customize-set-variable 'variable1 1)
(customize-set-variable 'variable2
(+ 1 1)))
:eval-after-load
((customize-set-variable 'variable3
(+ variable3 1))
(customize-set-variable 'variable2
(function-defined-in-feature-name))))

;; Expanded to:
(prog1 'feature-name
(with-eval-after-load 'feature-name
;; `variable3' is already defined.
(customize-set-variable 'variable3
(+ variable3 1))
;; `function-defined-in-feature-name' is already defined.
(customize-set-variable 'variable2
(function-defined-in-feature-name)))
(customize-set-variable 'variable1 1)
(customize-set-variable 'variable2
(+ 1 1)))
#+end_src

** =declare-function=, =defvar-noninitial=
These arguments declare functions and variables.
Each element of =declare-function= / =defvar-noninitial= is symbol as function/variable.
They exist in order to suppress warning of undefined functions/variables.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:declare-function
(function1
function2)
:defvar-noninitial
(variable1
variable2))

;; Expanded to:
(mic-core feature-name
:eval
((declare-function function1 "ext:feature-name")
(declare-function function2 "ext:feature-name")
(defvar variable1)
(defvar variable2))
:eval-after-load nil)

;; Expanded to:
(prog1 'feature-name
;; They declare that the functions `function1' and `function2' is defined in
;; the feature `feature-name'.
(declare-function function1 "ext:feature-name")
(declare-function function2 "ext:feature-name")
;; They declare that the variables `variable1' and `variable2' will be defined.
;; `defvar' without initial value declares symbol as variable.
(defvar variable1)
(defvar variable2))
#+end_src

** =:define-key=, =:define-key-after-load=, =:define-key-with-feature=
These arguments is transformed to =define-key= sexps.
On =:define-key= or =:define-key-after-load=, each element of the argument is
=(KEYMAP (KEYS . COMMAND)...)=. =KEYMAP= is keymap. =KEYS= is passed to =kbd=.
=COMMAND= is interactive function.

On =:define-key-with-feature=, each element is =(FEATURE (KEYMAP (KEYS . COMMAND)...))=.
=FEATURE= is feature, and the =define-key= sexp is evaluated after loading the =FEATURE=.
This exists in order to define =COMMAND= in the feature with =KEYS= to =KEYMAP= defined in =FEATURE=.
Use it to make sure that =KEYMAP= is defined.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:define-key
;; (KEYMAP (KEYS . COMMAND)...)
((global-map
;; #' is needed
("M-l" . #'feature-name-command1))
(prog-mode-map
;; #' is needed
("M-a" . #'feature-name-comman2)))

:define-key-after-load
;; When `feature-name-mode-map' is defined in `feature-name',
;; use `:define-key-after-load'.
((feature-name-mode-map
("M-r" . #'feature-name-command3)
("M-c" . #'feature-name-command4)))

;; When `other-feature-mode-map' is defined in `other-feature', which is not `feature-name',
;; use `:define-key-with-feature'.
:define-key-with-feature
((other-feature
(other-feature-mode-map
("M-q" . #'feature-name-command5)))))

;; Expanded to:
(mic-core feature-name
:eval
((define-key global-map (kbd "M-l") #'feature-name-command1)
(define-key prog-mode-map (kbd "M-a") #'feature-name-comman2)
(with-eval-after-load 'other-feature
(define-key other-feature-mode-map (kbd "M-q") #'feature-name-command5)))
:eval-after-load
((define-key feature-name-mode-map (kbd "M-r") #'feature-name-command3)
(define-key feature-name-mode-map (kbd "M-c") #'feature-name-command4)))

;; Expanded to:
(prog1 'feature-name
(with-eval-after-load 'feature-name
;; `:define-key-after-load'
(define-key feature-name-mode-map (kbd "M-r") #'feature-name-command3)
(define-key feature-name-mode-map (kbd "M-c") #'feature-name-command4))
;; `:define-key'
(define-key global-map (kbd "M-l") #'feature-name-command1)
(define-key prog-mode-map (kbd "M-a") #'feature-name-comman2)
;; `:define-key-with-feature'
(with-eval-after-load 'other-feature
(define-key other-feature-mode-map (kbd "M-q") #'feature-name-command5)))
#+end_src

** =:face=
This is transformed to =custom-set-faces= sexp.
Each element is =(FACE-SYMBOL . FACE-DEFINITION)=.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:face
((face-1
. ((t (:foreground "red" :height 10.0))))
(face-2
. ((t (:background "#006000" :foreground "white" :bold t))))))

;; Expanded to:
(mic-core feature-name
:eval
((custom-set-faces
'(face-1
((t (:foreground "red" :height 10.0))))
'(face-2
((t (:background "#006000" :foreground "white" :bold t))))))
:eval-after-load nil)

;; Expanded to:
(prog1 'feature-name
(custom-set-faces
'(face-1
((t (:foreground "red" :height 10.0))))
'(face-2
((t (:background "#006000" :foreground "white" :bold t))))))
#+end_src

** =:hook=
This is transformed to =add-hook= sexp.
Each element is =(HOOK . FUNCTION)=.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:hook
;; #' is needed
((hook1 . #'function1)
(hook2 . #'function2)
;; `lambda' is allowed (but not recommended)
(hook3 . (lambda (arg) 1))))

;; Expanded to:
(mic-core feature-name
:eval
((add-hook 'hook1 #'function1)
(add-hook 'hook2 #'function2)
(add-hook 'hook3 (lambda (arg) 1)))
:eval-after-load nil)

;; Expanded to:
(prog1 'feature-name
(add-hook 'hook1 #'function1)
(add-hook 'hook2 #'function2)
(add-hook 'hook3 (lambda (arg) 1)))
#+end_src

** =:package=
This is transformed to =package-install= sexps.
Each arguments are =PKG= used by =package-install=.

The expandation result is complicated, because it is annoying to fetch package archives many times.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:package
(package-name1
package-name2))

;; Expanded to:
(mic-core feature-name
:eval
;; When package is not installed
((unless (package-installed-p 'package-name1)
;; Ensure package is exists in archive
(when (assq 'package-name1 package-archive-contents)
(ignore-errors
(package-install 'package-name1)))
(unless (package-installed-p 'package-name1)
;; Refresh (fetch) new archive
(package-refresh-contents)
(condition-case _
(package-install 'package-name1)
(error
(warn "Package %s is not found" 'package-name1)))))

(unless (package-installed-p 'package-name2)
(when (assq 'package-name2 package-archive-contents)
(ignore-errors
(package-install 'package-name2)))
(unless (package-installed-p 'package-name2)
(package-refresh-contents)
(condition-case _
(package-install 'package-name2)
(error
(warn "Package %s is not found" 'package-name2))))))
:eval-after-load nil)

;; Expand to:
(prog1 'feature-name
(unless (package-installed-p 'package-name1)
(when (assq 'package-name1 package-archive-contents)
(ignore-errors
(package-install 'package-name1)))
(unless (package-installed-p 'package-name1)
(package-refresh-contents)
(condition-case _
(package-install 'package-name1)
(error
(warn "Package %s is not found" 'package-name1)))))
(unless (package-installed-p 'package-name2)
(when (assq 'package-name2 package-archive-contents)
(ignore-errors
(package-install 'package-name2)))
(unless (package-installed-p 'package-name2)
(package-refresh-contents)
(condition-case _
(package-install 'package-name2)
(error
(warn "Package %s is not found" 'package-name2))))))
#+end_src

** =:require=
This is transformed to =require= sexps.
Each element is feature symbol and required on =:eval=.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:require
(feat1
feat2))

;; Expand to:
(mic-core feature-name
:eval-installation nil
:eval
((require 'feat1)
(require 'feat2))
:eval-after-load nil)

;; Expand to:
(prog1 'feature-name
(require 'feat1)
(require 'feat2))
#+end_src

** =:require-after=
This is transformed to =require= sexps in =with-eval-after-load= section.
Each element is alist. =car= of each element is feature symbol which is
used as first argument of =with-eval-after-load=.
=cdr= of each element is list of features required after the =car=.

This is used when you should require package after another one but
there is no functions to call so =autoload= cannot be used.

#+begin_src emacs-lisp :tangle yes
(mic feature-name
:require-after
((feat-after1
. (feat1 feat2))
(feat-after2
feat3
feat4)))

;; Expand to:
(mic-core feature-name
:eval-installation nil
:eval
((with-eval-after-load 'feat-after1
(require 'feat1)
(require 'feat2))
(with-eval-after-load 'feat-after2
(require 'feat3)
(require 'feat4)))
:eval-after-load nil)

;; Expand to:
(prog1 'feature-name
(with-eval-after-load 'feat-after1
(require 'feat1)
(require 'feat2))
(with-eval-after-load 'feat-after2
(require 'feat3)
(require 'feat4)))
#+end_src

* Define your own =mic=
:PROPERTIES:
:CUSTOM_ID: define-your-own-mic
:END:
You do not like =mic= behavior? It is OK. You can define your own =mic=!
There are some ways to define it:
- Use =mic-defmic=
- Use =defmacro=

** Define your own =mic= with =mic-defmic=
If you would like to add keywords, or to make some keywords more simple,
you can define =filter= and apply it to =mic= (or =mic-core=, and another =mic=, any parent is allowed).

*** What is a filter?
The filter recieves one argument, =PLIST= (plist, property list), and returns =RETURNED-PLIST=.
It filters or transforms it into returned plist.
It is better to divide filters by every keyword, because of reusability.

1. Each filter recieves 1 argument =PLIST=, which is plist (property list).
2. Each filter returns =RETURNED-PLIST=, which is plist.
3. =PLIST= is given by user or filter before.
4. =PLIST= have feature name =:name= property.
5. =RETURNED-PLIST= is passed to next filter or parent =mic= (=mic=, =mic-core=, or another).
6. =RETURNED-PLIST= should have same value of =:name= property.
7. The property only used by your filter should be removed in =RETURNED-PLIST=.

Here is example:
#+begin_src emacs-lisp :tangle yes
(defun my-filter-global-set-key-without-quote (plist)
(let ((alist
;; Get value from your own keyword
(plist-get plist :bind))
sexps)
(setq sexps
;; Transform each element
(mapcar
(lambda (arg)
(let ((keys (car arg))
(command (cdr arg)))
`(global-set-key (kbd ,keys) #',command)))
alist))
;; Put sexps to `:eval' arguments
(mic-plist-put-append plist :eval sexps)
;; Don't forget to delete your own keyword!
;; When forget it, parent recieves it and may cause unexpected result.
(mic-plist-delete plist :bind)
plist))

;; `defmic' defines new `mic' (see "Define mic with mic-defmic" section for more infomation)
(mic-defmic yourmic
mic ; Derived from `mic'
:filters '(my-filter-global-set-key-without-quote))

;; Here is `yourmic' expression
(yourmic package-name
;; New keyword you added by `my-filter-global-set-key-without-quote'
:bind
(("M-a" . beginning-of-defun)
("M-e" . end-of-defun))
;; Of course keywords for `mic', which is original of `yourmic', is allowed.
:hook ((after-init-hook . #'ignore)))

;; Then first `PLIST' is:
'( :name package-name
:bind (("M-a" . beginning-of-defun)
("M-e" . end-of-defun))
:hook ((after-init-hook . #'ignore)))

;; When you expand the sexp before, the filter you defined is called like:
(my-filter-global-set-key-without-quote
'( :name package-name
:bind (("M-a" . beginning-of-defun)
("M-e" . end-of-defun))
:hook ((after-init-hook . #'ignore))))

;; It returns `RETURNED-PLIST':
'( :name package-name
:hook ((after-init-hook function ignore))
:eval
((global-set-key (kbd "M-a") #'beginning-of-defun)
(global-set-key (kbd "M-e") #'end-of-defun)))

;; The `RETURNED-PLIST' is passed to a next filter if exists.
;; You use only one filter in definition,
;; so it is expanded to:
(mic package-name
:hook ((after-init-hook . #'ignore))
:eval
((global-set-key (kbd "M-a") #'beginning-of-defun)
(global-set-key (kbd "M-e") #'end-of-defun)))
#+end_src

*** Pre-defined filters
Some pre-defined filter, unused by =mic= definition, are available in =mic-filter.el=.

**** Filters for package manager
- =mic-filter-ell-get=
- =mic-filter-straight=
- =mic-filter-quelpa=
For more infomation, see docstring of each filter.

#+begin_src emacs-lisp :tangle yes
;;; el-get
(mic-defmic mic-with-el-get mic
:filters '(mic-filter-el-get))

(mic-with-el-get hydra
:el-get ((hydra :repo "abo-abo/hydra" :fetcher github)))

;; Expanded to:
(mic hydra
:eval-installation
((el-get-bundle hydra :repo "abo-abo/hydra" :fetcher github)))
#+end_src

#+begin_src emacs-lisp :tangle yes
;;; quelpa
(mic-defmic mic-with-quelpa mic
:filters '(mic-filter-quelpa))

(mic-with-quelpa hydra
:quelpa ((hydra :repo "abo-abo/hydra" :fetcher github)))

;; Expanded to:
(mic hydra
:eval-installation
((quelpa
'(hydra :repo "abo-abo/hydra" :fetcher github))))
#+end_src

#+begin_src emacs-lisp :tangle yes
;;; straight
(mic-defmic mic-with-straight mic
:filters '(mic-filter-straight))

(mic-with-straight hydra
:straight ((hydra :repo "abo-abo/hydra" :host github)))

;; Expanded to:
(mic hydra
:eval-installation
((straight-use-package
'(hydra :repo "abo-abo/hydra" :host github))))
#+end_src

**** Key definition
- =mic-filter-define-key-general=, =mic-filter-general-define-key=
- =mic-filter-mykie=
- =mic-filter-hydra=
- =mic-filter-pretty-hydra=, =mic-filter-pretty-hydra+=
- =mic-filter-mode-hydra=

Here is summaries and examples for these filters.
See a docstring and definition of each filter for more information.

***** general.el
[[https://github.com/noctuid/general.el][general.el]] makes key definition more convenient.
There are some filters for integration with it:
- =mic-filter-define-key-general=
- =mic-filter-general-define-key=
The both are expanded to =general-define-key= call.

=mic-filter-define-key-general=, which uses a =:define-key-general= keyword, is compatible with =:define-key= keyword.
In the other words, the syntax like =((keymap (key . function)...)...)= is allowed but =general-define-key= is used as backend.

On the other hand, =mic-filter-general-define-key=, which uses =:general-define-key= keyword, uses =general-define-key= syntax.
So you can use =:keymap= or =:prefix= keyword. Each element of the value of =:general-define-key= is directly passed to =general-define-key=.

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-define-key-general mic
:filters
'(mic-filter-define-key-general))

(mic-with-define-key-general package-name
:define-key-general
((keymap1
("C-d" . #'func1)
("C-q" . #'func2))
(override
("C-a" . #'func3)
("C-e" . #'func4))))

;; Expanded to:
(mic package-name
:eval
((general-define-key :keymaps 'keymap1
"C-d" (function func1)
"C-q" (function func2))
(general-define-key :keymaps 'override
"C-a" (function func3)
"C-e" (function func4))))
#+end_src

***** Mykie.el
[[https://github.com/yuutayamada/mykie-el][Mykie.el]] is is multiplexer of key definition.
There is filter for mykie:
- =mic-filter-mykie=

=mic-filter-mykie=, which uses a =:mykie= keyword, creates =mykie:define-key= sexp.
Each element of the value on =:mykie= keyword is a cons cell like =((keymap (key [:keyword function1] ...)...)...)=.
=car= of each element, which is keymap, and each element of =cdr= of each element of the value is passed to =mykie:define-key=.

#+begin_src emacs-lisp :tangle yes

#+end_src

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-filter-mykie mic
:filters
'(mic-filter-mykie))

(mic-with-filter-mykie package-name
:mykie
((global-map
("C-w" :default hydra-window-resizer/body :region kill-region))))

;; Expanded to:
(mic package-name
:eval
((mykie:define-key global-map "C-w" :default hydra-window-resizer/body :region kill-region)))
#+end_src

***** Hydra
[[https://github.com/abo-abo/hydra][Hydra]] makes Emacs bindings stick around.
There is a filter for integration of Hydra:
- =mic-filter-hydra=

=mic-filter-hydra=, which uses a =:hydra= keyword, creates =defhydra= sexp.
Each element of the value on the =:hydra= keyword is passed to =defhydra= directly.

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-hydra mic
:filters '(mic-filter-hydra))

(mic-with-hydra package-name
:hydra
;; Spacing induces good indent
(( hydra-window-resizer ()
("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally")
("" shrink-window)
("" enlarge-window)
("" enlarge-window-horizontally)
("" shrink-window-horizontally)
("q" nil "quit"))))

;; Expanded to:
(mic package-name
:eval
((defhydra hydra-window-resizer nil
("p" shrink-window "shrink" :exit nil :cmd-name hydra-window-resizer/shrink-window :column nil)
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally")
("" shrink-window)
("" enlarge-window)
("" enlarge-window-horizontally)
("" shrink-window-horizontally)
("q" nil "quit"))))
#+end_src

***** pretty-hydra
[[https://github.com/jerrypnz/major-mode-hydra.el#pretty-hydra][Pretty Hydra]] defines prettier hydra.
There is some filters for integration of it:
- =mic-filter-pretty-hydra=
- =mic-filter-pretty-hydra+=

=mic-filter-pretty-hydra= uses =:pretty-hydra=, whereas =mic-filter-pretty-hydra+= uses =:pretty-hydra+=.
Each element is passed to =pretty-hydra-define=, which defines new hydra, or =pretty-hydra-define+=, which appends to existing hydra if exist.
The both have absolutely same syntax. Each element is passed to each defining macros directly.

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-pretty-hydra mic
:filters '(mic-filter-pretty-hydra
mic-filter-pretty-hydra+))

;;; `:pretty-hydra'
(mic-with-pretty-hydra package-name
:pretty-hydra
(( hydra-window-resizer ()
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window)
("" enlarge-window)
("" enlarge-window-horizontally)
("" shrink-window-horizontally))
"Quit"
("q" nil "quit")))))

;; Expanded to:
(mic package-name
:eval
((pretty-hydra-define hydra-window-resizer nil
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window "shrink-window")
("" enlarge-window "enlarge-window")
("" enlarge-window-horizontally "enlarge-window-horizontally")
("" shrink-window-horizontally "shrink-window-horizontally"))
"Quit"
("q" nil "quit")))))

;;; `:pretty-hydra+'
(mic-with-pretty-hydra package-name
:pretty-hydra+
(( hydra-window-resizer ()
("Vim-like"
(("h" enlarge-window-horizontally "enlarge-horizontally")
("j" shrink-window "shrink")
("k" enlarge-window "enlarge")
("l" shrink-window-horizontally "shrink-horizontally"))))))

;; Expanded to:
(mic package-name
:eval
((pretty-hydra-define+ hydra-window-resizer nil
("Vim-like"
(("h" enlarge-window-horizontally "enlarge-horizontally")
("j" shrink-window "shrink")
("k" enlarge-window "enlarge")
("l" shrink-window-horizontally "shrink-horizontally"))))))
#+end_src

***** major-mode-hydra
[[https://github.com/jerrypnz/major-mode-hydra.el#major-mode-hydra][Major Mode Hydra]] defines major-mode specific hydra function, =major-mode-hydra=.
There is a filter for integration of it:
- =mic-filter-mode-hydra=

=mic-filter-mode-hydra= uses a =:mode-hydra= keyword.
Each element of the value of the keyword is passed to =major-mode-hydra-define= directly.

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-mode-hydra mic
:filters '(mic-filter-mode-hydra
mic-filter-mode-hydra+))

;;; `:mode-hydra'
(mic-with-mode-hydra package-name
:mode-hydra
(( c-mode (:title "C Mode" :quit-key "q")
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window)
("" enlarge-window)
("" enlarge-window-horizontally)
("" shrink-window-horizontally))))))

;; Expanded to:
(mic package-name
:eval
((major-mode-hydra-define c-mode
(:title "C Mode" :quit-key "q")
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window "shrink-window")
("" enlarge-window "enlarge-window")
("" enlarge-window-horizontally "enlarge-window-horizontally")
("" shrink-window-horizontally "shrink-window-horizontally"))))))

;;; `:mode-hydra+'
(mic-with-mode-hydra package-name
:mode-hydra+
(( c-mode (:title "C Mode" :quit-key "q")
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window)
("" enlarge-window)
("" enlarge-window-horizontally)
("" shrink-window-horizontally))))))

;; Expanded to:
(mic package-name :eval
((major-mode-hydra-define+ c-mode
(:title "C Mode" :quit-key "q" :hint nil :color teal :separator "═")
("Alphabet"
(("p" shrink-window "shrink")
("n" enlarge-window "enlarge")
("f" enlarge-window-horizontally "enlarge-horizontally")
("b" shrink-window-horizontally "shrink-horizontally"))
"Arrow"
(("" shrink-window "shrink-window")
("" enlarge-window "enlarge-window")
("" enlarge-window-horizontally "enlarge-window-horizontally")
("" shrink-window-horizontally "shrink-window-horizontally"))))))
#+end_src

**** Alternative of filters

***** Hook
- =mic-filter-hook-list= ::
This is almost same as =mic-filter-hook=, but =car= of each element of the value should be list of hook,
and the =cdr= should be list of function (should be quoted).
=:hook-list= is used as keyword.
- =mic-filter-hook-list-maybe= ::
This is almost same as =mic-filter-hook=, but =car= of each element of the value should be list of hook or just one hook,
and the =cdr= should be list of function or just one function (should *NOT* be quoted).
=:hook-list-maybe= is used as keyword.
- =mic-filter-hook-quote= ::
This is almost same as =mic-filter-hook=, but =cdr= of each element of the value should not be quoted.
=:hook-quote= is used as keyword.

*** Helper for defining a filter
There are some helpers for defining a filter.

**** Utilities
Usually, a filter proceeds filtering by 4 steps:
1. Get data on a specific keyword in =PLIST=
2. Convert data to sexp
3. Append the sexp to value on =:eval= in =PLIST=
4. Delete the specific keyword from =PLIST=

There are some macros to help step 3. and 4. in =mic-utils.el=.
- =mic-plist-put-append=, which helps step 3., takes three arguments,
=PLIST=, =PROP=, which means keyword, and =VAL=.
It get a value on =PROP= in =PLIST=, and appends =VAL= to the value.
- =mic-plist-delete=, which helps step 4., takes one obligatory argument =PLIST=,
and extra arguments =PROPS=.
It removes =PROPS= keywords from =PLIST= and return it.

**** =deffilter=
To define a simple filter or to modify an existing filter,
you can use =mic-deffilter-*= macros in =mic-deffilter.el=.
See each macro definition and docstring for more information.

- =mic-deffilter-alias= :: Induce alias keyword.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-alias example-filter-alias :alias :origin)

(example-filter-alias '(:alias "Hello"))
;; =>
(:origin "Hello")
#+end_src
- =mic-deffilter-const= :: Put constant value on keyword.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-const example-filter-const
"Optional docstring."
:eval '((message "Hello")))

;; Add a :eval keyword when it does not exist.
(example-filter-const '(:other-keyword "Hi"))
;; =>
(:other-keyword "Hi" :eval ((message "Hello")))

;; Overwrite when a :eval keyword exists.
(example-filter-const '(:eval ((message "Good bye")) :other-keyword "Hi"))
;; =>
(:eval ((message "Hello")) :other-keyword "Hi")
#+end_src
- =mic-deffilter-const-append= :: Append constant value on keyword.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-const-append example-filter-const-append
:eval '((message "Hello")))

;; Same as `mic-deffilter-const' when any :eval keyword does not exist.
(example-filter-const-append '(:other-keyword "Hi"))
;; =>
(:other-keyword "Hi" :eval ((message "Hello")))

;; Append the value when the a :eval keyword exists.
(example-filter-const-append '(:eval ((message "Good bye")) :other-keyword "Hi"))
;; =>
(:eval ((message "Good bye") (message "Hello")) :other-keyword "Hi")
#+end_src
- =mic-deffilter-ignore= :: Just remove value on keyword.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-ignore example-filter-ignore
:ignore-me)

(example-filter-ignore '(:ignore-me "Ignored" :remain-me "Remained"))
;; =>
(:remain-me "Remained")
#+end_src
- =mic-deffilter-nonlist-to-list= :: If value is not list, wrap it into list.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-nonlist-to-list example-filter-nonlist-to-list
:package)

(example-filter-nonlist-to-list '(:package t))
;; =>
(:package (t))
#+end_src
- =mic-deffilter-replace-keyword-append= :: From an existing filter, define a new filter which uses another keywords as input and output.
Value is appended to the keyword for output.
#+begin_src emacs-lisp :tangle yes
;; Original filter: `mic-filter-mykie'
(mic-filter-mykie '(:mykie ((global-map ("C-a" :default beginning-of-line)))))
;; =>
(:eval ((mykie:define-key global-map "C-a" :default beginning-of-line)))

(mic-deffilter-replace-keyword-append example-filter-replace-keyword-append
mic-filter-mykie
:mykie-after-load :mykie
'((:eval . :eval-after-load)))

;; An input keyword and an output keyword is replaced
(example-filter-replace-keyword-append '(:mykie-after-load ((global-map ("C-a" :default beginning-of-line)))))
;; =>
(:eval-after-load ((mykie:define-key global-map "C-a" :default beginning-of-line)))
#+end_src
- =mic-deffilter-convert-after-load= :: From an existing filter, define a new filter which outputs an =:eval-after-load= keyword instead of =:eval=.
It is same as =(mic-deffilter-replace-keyword-append name filter old-keyword new-keyword '((:eval . :eval-after-load)))=.
#+begin_src emacs-lisp :tangle yes
;; Original filter: `mic-filter-mykie'
(mic-filter-mykie '(:mykie ((global-map ("C-a" :default beginning-of-line)))))
;; =>
(:eval ((mykie:define-key global-map "C-a" :default beginning-of-line)))

(mic-deffilter-convert-after-load example-filter-convert-after-load
mic-filter-mykie
:mykie-after-load :mykie)

;; An input keyword and an output keyword is replaced
(example-filter-convert-after-load '(:mykie-after-load ((global-map ("C-a" :default beginning-of-line)))))
;; =>
(:eval-after-load ((mykie:define-key global-map "C-a" :default beginning-of-line)))
#+end_src
- =mic-deffilter-t-to-name= :: Replace =t= with feature name in a list keyword.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-t-to-name example-filter-t-to-name
:replace)

;; :name keyword is needed in addition to :replace keyword
(example-filter-t-to-name '(:name feature-name :replace (1 2 3 t 5 6 t)))
;; =>
(:name feature-name :replace (1 2 3 feature-name 5 6 feature-name))
#+end_src
- =mic-deffilter-validate= :: Return a recieved plist except that it validates and sieves keyword in the plist
to confirm the returned plist has no invalid keywords.
#+begin_src emacs-lisp :tangle yes
(mic-deffilter-validate example-filter-validate
:name :key1 :key2)

(example-filter-validate '(:name feature-name :key1 "Hello" :key2 "Hi" :key3 "Bad" :key4 "Sad"))
;; =>
(:name feature-name :key1 "Hello" :key2 "Hi")
;; In addition, warnings are displayed like:
;; Warning (emacs): 'mic' feature-name: The keyword :key3 is not allowed by filter 'example-filter-validate'
;; Warning (emacs): 'mic' feature-name: The keyword :key4 is not allowed by filter 'example-filter-validate'
#+end_src

*** Define =mic= with =mic-defmic=
=mic-defmic= recieves arguments: =NAME=, =PANRENT=, optional =DOCSTRING=, keyword argument =FILTERS=.
=NAME= is your new =mic= macro name. =PARENT= is parent =mic=, which recieves =RETURNED-PLIST= at last.
=FILTERS= is list of your filters. When your =mic= recieves plist, the plist is filtered by all of your =FILTERS= in order,
then the plist is passed to =PARENT=.

Here is example:
#+begin_src emacs-lisp :tangle yes
;; Define `mymic'
(mic-defmic mymic
;; Parent is here. You can also use `mic-core'.
mic
:filters
'(my-filter-global-set-key-without-quote
;; You can add other filters below
)
;; You can comment out the line below to catch, warn and ignore errors.
;; :error-protection? t
)

;; Then you can use `mymic' like:
(mymic simple
:bind
(("C-d" . delete-forward-char)
("C-x l" . toggle-truncate-lines))
;; Of course parent keywords are accepted.
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50)))

;; Expanded to:
(mic simple
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50))
:eval
((global-set-key (kbd "C-d") #'delete-forward-char)
(global-set-key (kbd "C-x l") #'toggle-truncate-lines)))
#+end_src

When you would like to use =mic-core= as =PARENT=, =mic-filter-core-validate= is useful to validate plist.
*Please put it tail of =FILTERS= if you use it.*

**** Error protection
If you want your =mic= to catch, warn and dismiss errors and to continue evaluation, set =:error-protection?= =t=.
#+begin_src emacs-lisp :tangle yes
(mic-defmic mymic-with-error-protection
;; Parent is here. You can also use `mic-core'.
mic
:filters
'(my-filter-global-set-key-without-quote)
:error-protection? t)

(mymic-with-error-protection simple
:bind
(("C-d" . delete-forward-char)
("C-x l" . toggle-truncate-lines))
;; Of course parent keywords are accepted.
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50)))

;; Expanded to:
(condition-case-unless-debug error ; Catch error
(mic simple
:custom
((kill-whole-line . t)
(set-mark-command-repeat-pop . t)
(mark-ring-max . 50))
:eval
((global-set-key (kbd "C-d") (function delete-forward-char))
(global-set-key (kbd "C-x l") (function toggle-truncate-lines))))
;; Warn caught error but continue evaluation
(error
(warn "`%s' %s: evaluation error: %s" 'mymic-with-error-protection 'simple
(error-message-string error))))
#+end_src

**** Accept non-plist input
Like =use-package= and =leaf=, you can define =mic= which accepts non-plist input.
If you want to do so, you should pass =:inputter= argument to =mic-defmic=.
=INPUTTER= is a function which takes one argument =INPUT=, and transform it into =PLIST= as returned value.

Simply, you can use =mic-definputter-pseudo-plist= defined in [[file:mic-definputter.el]]
to define inputter like =use-package= or =leaf=.
it takes two arguments =NAME= and =LISTIZED-KEYWORDS=. =NAME= is a name of the inputter function,
and =LISTIZED-KEYWORDS= is list of keyword whose value can be passed multiple times.
#+begin_src emacs-lisp :tangle yes
(mic-definputter-pseudo-plist my-inputter
'(:eval :eval-after-load :define-key))

(mic-defmic mymic-with-inputter mic
:inputter #'my-inputter)

(mymic-with-inputter feature-name
:eval
;; Like `use-package', you can put multiple sexps after :eval, instead of list of sexp
(message "Hello")
(message "Good bye")

:eval-after-load
(message "Hello, after load")
(message "Good bye, after load")

;; Instead, list of sexp is not allowed
;; :eval-after-load
;; ((message "Hello, after load")
;; (message "Good bye, after load"))

:define-key
(global-map
("M-a" . #'beginning-of-defun))
(esc-map
("e" . #'end-of-defun))

;; Other keyword is not affected by inputter
:package
(ivy hydra))
#+end_src

**** Adopt a parent other than =mic=, =mic-core= and its derivation
You can use other configuration managers, such as [[https://github.com/jwiegley/use-package][use-package]] and [[https://github.com/conao3/leaf.el][leaf.el]].
However, filters defined by =mic= output keyword for =mic= family, such as =:eval=, =:eval-after-load=.
So you should tell =mic-defmic= how to adapt outputs to its parent by =:adapter= option.
The adapter takes one argument =PLIST=, and returns a list to pass to the parent.

Two adapter are pre-defined:
- =mic-adapter-use-package= :: Adapter for =use-package=.
- =mic-adapter-leaf= :: Adapter for =leaf=.

#+begin_src emacs-lisp :tangle yes
(mic-defmic mic-with-use-package use-package
:filters '(mic-filter-define-key-with-feature)
:adapter #'mic-adapter-use-package)

(mic-with-use-package feature-name
:define-key-with-feature
((org
(org-mode-map
("M-a" . #'feature-name-command))))
;; You can use `use-package' feature
:bind
(("M-a" . beginning-of-defun)
("M-e" . end-of-defun)))

;; Expanded to:
(use-package feature-name
:bind
(("M-a" . beginning-of-defun)
("M-e" . end-of-defun))
;; :defer is needed to wrap :config section around `eval-after-load'
:defer t
:init
(with-eval-after-load 'org
(define-key org-mode-map (kbd "M-a") (function feature-name-command))))
#+end_src

** Define your own =mic= with =defmacro=
When you read here, you should know =defmacro=.
You can do anything with =defmacro=. =mic-defmic= is easy way to define your =mic=,
but may be not enough for you, because of restriction. Then *I RECOMMEND to use =defmacro=*.
I am looking forward to seeing your =mic= defined by =defmacro=!

* Alternative
:PROPERTIES:
:CUSTOM_ID: alternative
:END:
There are some alternatives:
- [[https://github.com/jwiegley/use-package][=use-package=]]
- [[https://github.com/conao3/leaf.el][=leaf=]]

They are more easy to use, but sometimes have less expressive ability.
=mic= is more simple and has more expressive ability, but sometimes more redundant.
It is just your preference.

In addition, they are customizable, while =mic= is not customizable, but re-definable.
You can define your own =mic= according to your preference, with =mic= help.
Of course you can define your own =mic= with =use-package= or =leaf= as backend.

* Contribute
When you think you would like to share your filter or your own =mic=, use GitHub Discussion.
Of course your =mic= defined by =defmacro=. Any issue is welcome.

* License
This package is licensed by GPLv3. See [[file:LICENSE][LICENSE]].