#+TITLE: API Demos
#+PROPERTY: header-args:elisp :results pp

This file contains demos of Doom's public API; its core library, macros, and
autodefs. It is used by the =elisp-demos= package to display examples of their
usage from their documentation (e.g. =SPC h f add-hook\!=).

* Table of Contents :TOC_3:
- [[#core-lib][core-lib]]
  - [[#add-hook][add-hook!]]
  - [[#add-transient-hook][add-transient-hook!]]
  - [[#after][after!]]
  - [[#appendq][appendq!]]
  - [[#custom-set-faces][custom-set-faces!]]
  - [[#custom-theme-set-faces][custom-theme-set-faces!]]
  - [[#defer-feature][defer-feature!]]
  - [[#defer-until][defer-until!]]
  - [[#disable-packages][disable-packages!]]
  - [[#doom][doom!]]
  - [[#file-exists-p][file-exists-p!]]
  - [[#cmd][cmd!]]
  - [[#cmd-1][cmd!!]]
  - [[#cmds][cmds!]]
  - [[#kbd][kbd!]]
  - [[#letenv][letenv!]]
  - [[#load][load!]]
  - [[#map][map!]]
  - [[#package][package!]]
  - [[#pushnew][pushnew!]]
  - [[#prependq][prependq!]]
  - [[#quiet][quiet!]]
  - [[#remove-hook][remove-hook!]]
  - [[#setq][setq!]]
  - [[#setq-hook][setq-hook!]]
  - [[#unsetq-hook][unsetq-hook!]]
  - [[#use-package][use-package!]]

* core-lib
** add-hook!
#+BEGIN_SRC elisp :eval no
;; With only one hook and one function, this is identical to `add-hook'. In that
;; case, use that instead.
(add-hook! 'some-mode-hook #'enable-something)

;; Adding many-to-many functions to hooks
(add-hook! some-mode #'enable-something #'and-another)
(add-hook! some-mode #'(enable-something and-another))
(add-hook! '(one-mode-hook second-mode-hook) #'enable-something)
(add-hook! (one-mode second-mode) #'enable-something)

;; Appending and local hooks
(add-hook! (one-mode second-mode) :append #'enable-something)
(add-hook! (one-mode second-mode) :local #'enable-something)

;; With arbitrary forms
(add-hook! (one-mode second-mode) (setq v 5) (setq a 2))
(add-hook! (one-mode second-mode) :append :local (setq v 5) (setq a 2))

;; Inline named hook functions
(add-hook! '(one-mode-hook second-mode-hook)
  (defun do-something ()
    ...)
  (defun do-another-thing ()
    ...))
#+END_SRC

** TODO add-transient-hook!
** after!
#+BEGIN_SRC elisp :eval no
;;; `after!' will take:

;; An unquoted package symbol (the name of a package)
(after! helm ...)

;; An unquoted list of package symbols (i.e. BODY is evaluated once both magit
;; and git-gutter have loaded)
(after! (magit git-gutter) ...)

;; An unquoted, nested list of compound package lists, using any combination of
;; :or/:any and :and/:all
(after! (:or package-a package-b ...)  ...)
(after! (:and package-a package-b ...) ...)
(after! (:and package-a (:or package-b package-c) ...) ...)
;; (Without :or/:any/:and/:all, :and/:all are implied.)

;; A common mistake is to pass it the names of major or minor modes, e.g.
(after! rustic-mode ...)
(after! python-mode ...)
;; But the code in them will never run! rustic-mode is in the `rustic' package
;; and python-mode is in the `python' package. This is what you want:
(after! rustic ...)
(after! python ...)
#+END_SRC
** appendq!
#+BEGIN_SRC elisp
(let ((x '(a b c)))
  (appendq! x '(c d e))
  x)
#+END_SRC

#+RESULTS:
: (a b c c d e)

#+BEGIN_SRC elisp
(let ((x '(a b c))
      (y '(c d e))
      (z '(f g)))
  (appendq! x y z '(h))
  x)
#+END_SRC

#+RESULTS:
: (a b c c d e f g h)

** custom-set-faces!
#+BEGIN_SRC elisp :eval no
(custom-set-faces!
 '(outline-1 :weight normal)
 '(outline-2 :weight normal)
 '(outline-3 :weight normal)
 '(outline-4 :weight normal)
 '(outline-5 :weight normal)
 '(outline-6 :weight normal)
 '(default :background "red" :weight bold)
 '(region :background "red" :weight bold))

(custom-set-faces!
 '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6)
   :weight normal)
 '((default region)
   :background "red" :weight bold))

(let ((red-bg-faces '(default region)))
  (custom-set-faces!
   `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i)))
     :weight normal)
   `(,red-bg-faces
     :background "red" :weight bold)))

;; You may utilise `doom-themes's theme API to fetch or tweak colors from their
;; palettes. No need to wait until the theme or package is loaded. e.g.
(custom-set-faces!
 `(outline-1 :foreground ,(doom-color 'red))
 `(outline-2 :background ,(doom-color 'blue)))
#+END_SRC

** custom-theme-set-faces!
#+BEGIN_SRC elisp :eval no
(custom-theme-set-faces! 'doom-one
 '(outline-1 :weight normal)
 '(outline-2 :weight normal)
 '(outline-3 :weight normal)
 '(outline-4 :weight normal)
 '(outline-5 :weight normal)
 '(outline-6 :weight normal)
 '(default :background "red" :weight bold)
 '(region :background "red" :weight bold))

(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme)
 '((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6)
   :weight normal)
 '((default region)
   :background "red" :weight bold))

(let ((red-bg-faces '(default region)))
  (custom-theme-set-faces! '(doom-one-theme doom-one-light-theme)
   `(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i)))
     :weight normal)
   `(,red-bg-faces
     :background "red" :weight bold)))

;; You may utilise `doom-themes's theme API to fetch or tweak colors from their
;; palettes. No need to wait until the theme or package is loaded. e.g.
(custom-theme-set-faces! 'doom-one
 `(outline-1 :foreground ,(doom-color 'red))
 `(outline-2 :background ,(doom-color 'blue)))
#+END_SRC

** TODO defer-feature!
** TODO defer-until!
** disable-packages!
#+BEGIN_SRC elisp :eval no
;; Disable packages enabled by DOOM
(disable-packages! some-package second-package)
#+END_SRC

** doom!
#+BEGIN_SRC elisp :eval no
(doom! :completion
       company
       ivy
       ;;helm

       :tools
       (:if IS-MAC macos)
       docker
       lsp

       :lang
       (cc +lsp)
       (:cond ((string= system-name "work-pc")
               python
               rust
               web)
              ((string= system-name "writing-pc")
               (org +dragndrop)
               ruby))
       (:if IS-LINUX
           (web +lsp)
         web)

       :config
       literate
       (default +bindings +smartparens))
#+END_SRC

** file-exists-p!
#+BEGIN_SRC elisp
(file-exists-p! "init.el" doom-emacs-dir)
#+END_SRC

#+RESULTS:
: /home/hlissner/.emacs.d/init.el

#+BEGIN_SRC elisp
(file-exists-p! (and (or "doesnotexist" "init.el")
                     "LICENSE")
                doom-emacs-dir)
#+END_SRC

#+RESULTS:
: /home/hlissner/.emacs.d/LICENSE

** cmd!
#+BEGIN_SRC elisp :eval no
(map! "C-j" (cmd! (newline) (indent-according-to-mode)))
#+END_SRC

** cmd!!
When ~newline~ is passed a numerical prefix argument (=C-u 5 M-x newline=), it
inserts N newlines. We can use ~cmd!!~ to easily create a keybinds that bakes in
the prefix arg into the command call:

#+BEGIN_SRC elisp :eval no
(map! "C-j" (cmd!! #'newline 5))
#+END_SRC

Or to create aliases for functions that behave differently:

#+BEGIN_SRC elisp :eval no
(fset 'insert-5-newlines (cmd!! #'newline 5))

;; The equivalent of C-u M-x org-global-cycle, which resets the org document to
;; its startup visibility settings.
(fset 'org-reset-global-visibility (cmd!! #'org-global-cycle '(4))
#+END_SRC

** cmds!
#+BEGIN_SRC elisp :eval no
(map! :i [tab] (cmds! (and (featurep! :editor snippets)
                           (bound-and-true-p yas-minor-mode)
                           (yas-maybe-expand-abbrev-key-filter 'yas-expand))
                      #'yas-expand
                      (featurep! :completion company +tng)
                      #'company-indent-or-complete-common)
      :m [tab] (cmds! (and (bound-and-true-p yas-minor-mode)
                           (evil-visual-state-p)
                           (or (eq evil-visual-selection 'line)
                               (not (memq (char-after) (list ?\( ?\[ ?\{ ?\} ?\] ?\))))))
                      #'yas-insert-snippet
                      (and (featurep! :editor fold)
                           (save-excursion (end-of-line) (invisible-p (point))))
                      #'+fold/toggle
                      (fboundp 'evil-jump-item)
                      #'evil-jump-item))
#+END_SRC

** kbd!
#+BEGIN_SRC elisp :eval no
(map! "," (kbd! "SPC")
      ";" (kbd! ":"))
#+END_SRC

** letenv!
#+BEGIN_SRC elisp
(letenv! (("SHELL" "/bin/sh"))
  (shell-command-to-string "echo $SHELL"))
#+END_SRC

#+RESULTS:
: "/bin/sh\n"

** load!
#+BEGIN_SRC elisp :eval no
;;; Lets say we're in ~/.doom.d/config.el
(load! "lisp/module")                  ; loads ~/.doom.d/lisp/module.el
(load! "somefile" doom-emacs-dir)      ; loads ~/.emacs.d/somefile.el
(load! "anotherfile" doom-private-dir) ; loads ~/.doom.d/anotherfile.el

;; If you don't want a `load!' call to throw an error if the file doesn't exist:
(load! "~/.maynotexist" nil t)
#+END_SRC

** map!
#+BEGIN_SRC elisp :eval no
(map! :map magit-mode-map
      :m  "C-r" 'do-something           ; C-r in motion state
      :nv "q" 'magit-mode-quit-window   ; q in normal+visual states
      "C-x C-r" 'a-global-keybind
      :g "C-x C-r" 'another-global-keybind  ; same as above

      (:when IS-MAC
        :n "M-s" 'some-fn
        :i "M-o" (cmd! (message "Hi"))))

(map! (:when (featurep! :completion company) ; Conditional loading
        :i "C-@" #'+company/complete
        (:prefix "C-x"                       ; Use a prefix key
          :i "C-l" #'+company/whole-lines)))

(map! (:when (featurep! :lang latex)    ; local conditional
        (:map LaTeX-mode-map
          :localleader                  ; Use local leader
          :desc "View" "v" #'TeX-view)) ; Add which-key description
      :leader                           ; Use leader key from now on
      :desc "Eval expression" ";" #'eval-expression)
#+END_SRC

These are side-by-side comparisons, showing how to bind keys with and without
~map!~:

#+BEGIN_SRC elisp :eval no
;; bind a global key
(global-set-key (kbd "C-x y") #'do-something)
(map! "C-x y" #'do-something)

;; bind a key on a keymap
(define-key emacs-lisp-mode-map (kbd "C-c p") #'do-something)
(map! :map emacs-lisp-mode-map "C-c p" #'do-something)

;; unbind a key defined elsewhere
(define-key lua-mode-map (kbd "SPC m b") nil)
(map! :map lua-mode-map "SPC m b" nil)

;; bind multiple keys
(global-set-key (kbd "C-x x") #'do-something)
(global-set-key (kbd "C-x y") #'do-something-else)
(global-set-key (kbd "C-x z") #'do-another-thing)
(map! "C-x x" #'do-something
      "C-x y" #'do-something-else
      "C-x z" #'do-another-thing)

;; bind global keys in normal mode
(evil-define-key* 'normal 'global
  (kbd "C-x x") #'do-something
  (kbd "C-x y") #'do-something-else
  (kbd "C-x z") #'do-another-thing)
(map! :n "C-x x" #'do-something
      :n "C-x y" #'do-something-else
      :n "C-x z" #'do-another-thing)

;; or on a deferred keymap
(evil-define-key 'normal emacs-lisp-mode-map
  (kbd "C-x x") #'do-something
  (kbd "C-x y") #'do-something-else
  (kbd "C-x z") #'do-another-thing)
(map! :map emacs-lisp-mode-map
      :n "C-x x" #'do-something
      :n "C-x y" #'do-something-else
      :n "C-x z" #'do-another-thing)

;; or multiple maps
(dolist (map (list emacs-lisp-mode go-mode-map ivy-minibuffer-map))
  (evil-define-key '(normal insert) map
    "a" #'a
    "b" #'b
    "c" #'c))
(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map)
      :ni "a" #'a
      :ni "b" #'b
      :ni "c" #'c)

;; or in multiple states (order of states doesn't matter)
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
(evil-define-key* 'insert emacs-lisp-mode-map (kbd "C-x x") #'do-something-else)
(evil-define-key* '(visual normal insert emacs) emacs-lisp-mode-map (kbd "C-x z") #'do-another-thing)
(map! :map emacs-lisp-mode
      :nv   "C-x x" #'do-something      ; normal+visual
      :i    "C-x y" #'do-something-else ; insert
      :vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs

;; You can nest map! calls:
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
(evil-define-key* 'normal go-lisp-mode-map (kbd "C-x x") #'do-something-else)
(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
      (:map go-lisp-mode    :n  "C-x x" #'do-something-else))
#+END_SRC

** package!
#+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources
;; specified in `straight-recipe-repositories':
(package! evil)
(package! js2-mode)
(package! rainbow-delimiters)

;; To disable a package included with Doom (which will no-op all its `after!'
;; and `use-package!' blocks):
(package! evil :disable t)
(package! rainbow-delimiters :disable t)

;; To install a package from a github repo
(package! so-long :recipe (:host github :repo "hlissner/emacs-so-long"))

;; If a package is particularly big and comes with submodules you don't need,
;; you can tell the package manager not to clone the repo recursively:
(package! ansible :recipe (:nonrecursive t))

;; To pin a package to a specific commit:
(package! evil :pin "e7bc39de2f9")
;; ...or branch:
(package! evil :recipe (:branch "stable"))
;; To unpin a pinned package:
(package! evil :pin nil)

;; If you share your config between two computers, and don't want bin/doom
;; refresh to delete packages used only on one system, use :ignore
(package! evil :ignore (not (equal system-name "my-desktop")))
#+END_SRC

** pushnew!
#+BEGIN_SRC elisp
(let ((list '(a b c)))
  (pushnew! list 'c 'd 'e)
  list)
#+END_SRC

#+RESULTS:
: (e d a b c)

** prependq!
#+BEGIN_SRC elisp
(let ((x '(a b c)))
  (prependq! x '(c d e))
  x)
#+END_SRC

#+RESULTS:
: (c d e a b c)

#+BEGIN_SRC elisp
(let ((x '(a b c))
      (y '(c d e))
      (z '(f g)))
  (prependq! x y z '(h))
  x)
#+END_SRC

#+RESULTS:
: (c d e f g h a b c)

** quiet!
#+BEGIN_SRC elisp :eval no
;; Enters recentf-mode without extra output
(quiet! (recentf-mode +1))
#+END_SRC
** remove-hook!
#+BEGIN_SRC elisp :eval no
;; With only one hook and one function, this is identical to `remove-hook'. In
;; that case, use that instead.
(remove-hook! 'some-mode-hook #'enable-something)

;; Removing N functions from M hooks
(remove-hook! some-mode #'enable-something #'and-another)
(remove-hook! some-mode #'(enable-something and-another))
(remove-hook! '(one-mode-hook second-mode-hook) #'enable-something)
(remove-hook! (one-mode second-mode) #'enable-something)

;; Removing buffer-local hooks
(remove-hook! (one-mode second-mode) :local #'enable-something)

;; Removing arbitrary forms (must be exactly the same as the definition)
(remove-hook! (one-mode second-mode) (setq v 5) (setq a 2))
#+END_SRC
** setq!
#+BEGIN_SRC elisp
;; Each of these have a setter associated with them, which must be triggered in
;; order for their new values to have an effect.
(setq! evil-want-Y-yank-to-eol nil
       evil-want-C-u-scroll nil
       evil-want-C-d-scroll nil)
#+END_SRC
** setq-hook!
#+BEGIN_SRC elisp :eval no
;; Set multiple variables after a hook
(setq-hook! 'markdown-mode-hook
  line-spacing 2
  fill-column 80)

;; Set variables after multiple hooks
(setq-hook! '(eshell-mode-hook term-mode-hook)
  hscroll-margin 0)
#+END_SRC

** unsetq-hook!
#+BEGIN_SRC elisp :eval no
(unsetq-hook! 'markdown-mode-hook line-spacing)

;; Removes the following variable hook
(setq-hook! 'markdown-mode-hook line-spacing 2)

;; Removing N variables from M hooks
(unsetq-hook! some-mode enable-something and-another)
(unsetq-hook! some-mode (enable-something and-another))
(unsetq-hook! '(one-mode-hook second-mode-hook) enable-something)
(unsetq-hook! (one-mode second-mode) enable-something)
#+END_SRC

** use-package!
#+BEGIN_SRC elisp :eval no
;; Use after-call to load package before hook
(use-package! projectile
  :after-call (pre-command-hook after-find-file dired-before-readin-hook))

;; defer recentf packages one by one
(use-package! recentf
  :defer-incrementally easymenu tree-widget timer
  :after-call after-find-file)

;; This is equivalent to :defer-incrementally (abc)
(use-package! abc
  :defer-incrementally t)
#+END_SRC
