dotfiles/.config/doom/config.org

30 KiB
Raw Permalink Blame History

Doom Emacs Config

Basics & Interface

Basics

(setq user-full-name "hedy"
      user-mail-address "hedy@tilde.cafe")

Can set to relative or nil

(setq diplay-line-numbers-type t)

Seriously? I just want to quit. Damn. Why confirm lol

(setq confirm-kill-emacs nil)

Scroll margin like scrolloff in vim (not tangled)

(setq scroll-margin 6)

Horizontal scrolling is pain

(global-visual-line-mode t)

Give it the IDE vibes

; (run-with-timer 1 nil (lambda () (save-selected-window (treemacs))))

Indentation

(setq evil-shift-width 2)
(setq tab-width 4)

Forgot why this was needed…

(defun set-exec-path-from-shell-PATH ()
  "Set up Emacs' `exec-path' and PATH environment variable to match that used by the user's shell.

This is particularly useful under Mac OS X and macOS, where GUI apps are not started from a shell."
  (interactive)
  (let ((path-from-shell
         (replace-regexp-in-string "[ \t\n]*$" ""
          (shell-command-to-string ;;SHELL = fish
           "$SHELL --login -c 'string join : $PATH'"))))
    (setenv "PATH" path-from-shell)
    (setq exec-path (split-string path-from-shell path-separator))))

(set-exec-path-from-shell-PATH)

UI

(setq doom-modeline-height 40)

Window management

;; https://www.emacswiki.org/emacs/ToggleWindowSplit
(defun toggle-window-split ()
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  'split-window-horizontally
                'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

(define-key ctl-x-4-map "t" 'toggle-window-split)

Font

Doom exposes five (optional) variables for controlling fonts in Doom. Here are the three important ones:

  • doom-font
  • doom-variable-pitch-font
  • doom-big-font used for doom-big-font-mode; use this for presentations or streaming.

They all accept either a font-spec, font string ("Input Mono-12"), or xlfd font string. You generally only need these two:

(setq doom-font (font-spec :family "monospace" :size 12 :weight 'semi-light)
      doom-variable-pitch-font (font-spec :family "sans" :size 13))

Here is my configuration:

(setq doom-font (font-spec :family "Fira Code" :size 16 :weight 'normal)
      doom-variable-pitch-font (font-spec :family "Open Sans" :size 18 :weight 'light))

Theme

There are two ways to load a theme. Both assume the theme is installed and available. You can either set doom-theme or manually load a theme with the load-thee function.

(setq doom-theme 'doom-vibrant) ;; Doom's dracula is a bit funny

Configuration macros

Here are some additional functions/macros that could help you configure Doom:

  • load! for loading external *.el files relative to this one
  • use-package! for configuring packages
  • after! for running code after a package has loaded
  • add-load-path! for adding directories to the load-path, relative to this file. Emacs searches the load-path when you load packages with require or use-package.
  • map! for binding new keys

Evil

(setq evil-split-window-below t
      evil-vsplit-window-right t)

FIXING EVIL YANK DON'T USE CLIPBOARD

Workaround from:

https://discourse.doomemacs.org/t/how-to-set-up-clipboard/3742

;; don't put deleted strings to X11 clipboard
(setq select-enable-clipboard nil)
;; copying and pasting selected blocks in visual mode to and from X11 clipboard
(map! "S-C-c" #'clipboard-kill-ring-save)
(map! "S-C-v" #'clipboard-yank)

How to use yank/paste and system clipboard:

  • Anything copied outside of emacs, paste in emacs with S-C-v
  • Yank within emacs, will not override clipboard outside emacs
  • To paste yanks within emacs, use default paste bind or use p
  • Copy sth to clipboard from emacs: Use S-C-c, paste outside with normal system bind

Org

(setq org-directory "~/org/")

Note that there's another directory setting at /hedy/dotfiles/src/branch/master/.config/doom/Org%20roam%20&%20Org%20UI.

(org-babel-do-load-languages
 'org-babel-load-languages
 '((emacs-lisp . t)
   (python . t)))  ;; Why jupyter when you have this JK

Less "contained" org plugins (in the /hedy/dotfiles/src/branch/master/.config/doom/Plugins heading):

Also see /hedy/dotfiles/src/branch/master/.config/doom/Org%20latex

Org todo

I don't use org-agenda anymore so I won't be tangling this.

(setq org-todo-keyword-faces
      '(("NOW" . "labelColor") ("CANCELED" . "systemRedColor") ("DONE" . "selectedControlColor") ("FINISH" . "selectedControlColor") ("PAST" . "controlTextColor")
        ("RECUR" . "systemYellowColor") ("MARK" . "systemOrangeColor") ("PLAN" . "systemBrownColor")
        ("OVERDUE". "systemRedColor") ("DUE" . "systemYellowColor") ("STARTED" . "labelColor")))
(setq diary-file "~/Documents/diary/diary")

Org bullets

(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))

Plugins

Treemacs

(after! treemacs
  (setq treemacs-width 20
        treemacs-project-follow-cleanup t)
  (treemacs-load-theme "Default"))

Magit

Maybe I don't need this anymore? (Not tangled)

(after! magit
  (custom-set-faces
   '(magit-diff-added-highlight ((((type tty)) (:background nil))))
   '(magit-diff-context-highlight ((((type tty)) (:background nil))))
   '(magit-diff-file-heading ((((type tty)) nil)))
   '(magit-diff-removed ((((type tty)) (:foreground "red"))))
   '(magit-diff-removed-highlight ((((type tty)) (:background nil))))
   '(magit-section-highlight ((((type tty)) nil)))
   '(magit-diff-highlight-hunk-body ((((type tty)) (:background nil))))
   '(magit-diff-base-highlight ((((type tty)) (:background nil))))))

Elfeed

Note that I don't use elfeed anymore

;; Elfeed: Use sans for articles
(add-hook 'elfeed-show-mode-hook
      (lambda () (buffer-face-set 'variable-pitch)))

Vterm

This is no longer tangled as I sort of realized vterm + evil is kinda good.

Good old neovim terminal vibes.

(add-hook 'vterm-mode-hook
      (lambda () (visual-line-mode) (turn-off-evil-mode)))
(add-hook! 'vterm-mode-hook 'evil-insert)

Company

Completion everywhere is annoying. Period.

Enable with C-x C-o or something, check with C-x C-h in insert mode.

(after! company
  (setq company-idle-delay nil))
(after! company-quickhelp
  (setq company-quickhelp-mode t))
(use-package! company
  :config
  (setq company-tooltip-align-annotations nil)
  (setq company-icon-size '(auto-scale . 64))
  (setq company-box-icons-all-the-icons
        (let ((all-the-icons-scale-factor 10.0))
          `((Unknown       . ,(all-the-icons-material "find_in_page"             :face 'all-the-icons-purple))
            (Text          . ,(all-the-icons-material "text_fields"              :face 'all-the-icons-green))
            (Method        . ,(all-the-icons-material "functions"                :face 'all-the-icons-red))
            (Function      . ,(all-the-icons-material "functions"                :face 'all-the-icons-red))
            (Constructor   . ,(all-the-icons-material "functions"                :face 'all-the-icons-red))
            (Field         . ,(all-the-icons-material "functions"                :face 'all-the-icons-red))
            (Variable      . ,(all-the-icons-material "adjust"                   :face 'all-the-icons-blue))
            (Class         . ,(all-the-icons-material "class"                    :face 'all-the-icons-red))
            (Interface     . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red))
            (Module        . ,(all-the-icons-material "view_module"              :face 'all-the-icons-red))
            (Property      . ,(all-the-icons-material "settings"                 :face 'all-the-icons-red))
            (Unit          . ,(all-the-icons-material "straighten"               :face 'all-the-icons-red))
            (Value         . ,(all-the-icons-material "filter_1"                 :face 'all-the-icons-red))
            (Enum          . ,(all-the-icons-material "plus_one"                 :face 'all-the-icons-red))
            (Keyword       . ,(all-the-icons-material "filter_center_focus"      :face 'all-the-icons-red))
            (Snippet       . ,(all-the-icons-material "short_text"               :face 'all-the-icons-red))
            (Color         . ,(all-the-icons-material "color_lens"               :face 'all-the-icons-red))
            (File          . ,(all-the-icons-material "insert_drive_file"        :face 'all-the-icons-red))
            (Reference     . ,(all-the-icons-material "collections_bookmark"     :face 'all-the-icons-red))
            (Folder        . ,(all-the-icons-material "folder"                   :face 'all-the-icons-red))
            (EnumMember    . ,(all-the-icons-material "people"                   :face 'all-the-icons-red))
            (Constant      . ,(all-the-icons-material "pause_circle_filled"      :face 'all-the-icons-red))
            (Struct        . ,(all-the-icons-material "streetview"               :face 'all-the-icons-red))
            (Event         . ,(all-the-icons-material "event"                    :face 'all-the-icons-red))
            (Operator      . ,(all-the-icons-material "control_point"            :face 'all-the-icons-red))
            (TypeParameter . ,(all-the-icons-material "class"                    :face 'all-the-icons-red))
            (Template      . ,(all-the-icons-material "short_text"               :face 'all-the-icons-green))
            (ElispFunction . ,(all-the-icons-material "functions"                :face 'all-the-icons-red))
            (ElispVariable . ,(all-the-icons-material "check_circle"             :face 'all-the-icons-blue))
            (ElispFeature  . ,(all-the-icons-material "stars"                    :face 'all-the-icons-orange))
            (ElispFace     . ,(all-the-icons-material "format_paint"             :face 'all-the-icons-pink)))))
)

Inline math symbols with latex

Completion for inserting Unicode symbols.

Type: \al<C-x RET>, and you'll get some suggestions like aleph ℵ, alpha α; accept one and what you typed will be replaced with that symbol.

As with before you should check with C-x C-h in insert mode for all options.

(require 'math-symbol-lists)
;; This is actually for C-\, then select input "math",
;; then the Ω will show in the status bar.
(quail-define-package "math" "UTF-8" "Ω" t)
;; (quail-define-rules ; add whatever extra rules you want to define here...
;;  ("\\from"    #X2190)
;;  ("\\to"      #X2192)
;;  ("\\lhd"     #X22B2)
;;  ("\\rhd"     #X22B3)
;;  ("\\unlhd"   #X22B4)
;;  ("\\unrhd"   #X22B5))
(mapc (lambda (x)
        (if (cddr x)
            (quail-defrule (cadr x) (car (cddr x)))))
      (append math-symbol-list-basic math-symbol-list-extended))

(add-to-list 'company-backends 'company-math-symbols-unicode)

Emoji

;; Emoji completion
(defun --set-emoji-font (frame)
  "Adjust the font settings of FRAME so Emacs can display emoji properly."
  (if (eq system-type 'darwin)
      ;; For NS/Cocoa
      (set-fontset-font t 'symbol (font-spec :family "Apple Color Emoji") frame 'prepend)
    ;; For Linux
    (set-fontset-font t 'symbol (font-spec :family "Symbola") frame 'prepend)))

;; For when Emacs is started in GUI mode:
(--set-emoji-font nil)
;; Hook for when a frame is created with emacsclient
;; see https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Frames.html
(add-hook 'after-make-frame-functions '--set-emoji-font)
(require 'company-emoji)
(add-to-list 'company-backends 'company-emoji)

Wrap region

This is essentially like vim-surround but more modern-IDE-like, y'know, how you can select some text in VS Code, press " and your selection is wrapped with quotes on both ends.

With evil mode on, select some text within evil's insert mode, then use quotes or brackets to surround selected region with those characters.

Below are definitions of more wrappers.

(use-package! wrap-region
  :config
  (wrap-region-add-wrappers
   '(("/* " " */" "#" (java-mode javascript-mode css-mode))
     ("`" "`" nil (markdown-mode org-mode))
     ("=" "=" nil (org-mode))
     ("~" "~" nil (org-mode))
     ("*" "*" nil (markdown-mode org-mode)))))

(add-hook! ('org-mode 'markdown-mode) 'wrap-region-mode)

This allows you to select some text in insert mode, press ` and it will be wrapped with backticks on both sides, for example.

IMO this is friendly and customizable than vim-surround.

Org roam & Org UI

(use-package! org-roam
  :ensure t
  :custom
  (org-roam-directory (file-truename "~/org/orgroam"))
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n g" . org-roam-graph)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ;; Dailies
         ("C-c n j" . org-roam-dailies-capture-today))
  :config
  ;; If you're using a vertical completion framework, you might want a more informative completion interface
  (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
  (org-roam-db-autosync-mode)
  ;; If using org-roam-protocol
  (require 'org-roam-protocol))

(use-package! websocket
  :after org-roam)

(use-package! org-roam-ui
    :after org-roam ;; or :after org
;;         normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;;         a hookable mode anymore, you're advised to pick something yourself
;;         if you don't care about startup time, use
;;  :hook (after-init . org-roam-ui-mode)
    :config
    (setq org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start nil))

Org anki

(customize-set-variable 'org-anki-default-deck "Doom")

Below is from:

https://jeffkreeftmeijer.com/org-unable-to-resolve-link

(defun my/org-anki-sync-fix-refs ()
  "Fix 'Unable to resolve link: XXX'"
  (interactive)
  (org-id-update-id-locations
   (directory-files-recursively org-roam-directory "\\.org$")))

Templates

org-anki template for card front side with breadcumb of org file headings and a link to open the file in emacs from where the card is created from.

(customize-set-variable 'org-anki-field-templates
 '(("Basic"
    ("Front" . (lambda (it)        ;; strip text-properties
                (let ((breadcrumb (substring-no-properties
                                   ;; arguments:
                                   ;; - prepend file name?
                                   ;; - incl current heading?
                                   ;; - SEPARATOR
                                   ;; - return as string?
                                   (org-display-outline-path nil nil " > " t)))
                      (path (buffer-file-name)))
                  ;; "File Title: Heading > SubHeading > Sub-subheading"
                  (setq breadcrumb (concat (org-get-title) ": " breadcrumb))
                  (concat
                   (format
                    (concat "<div id=\"meta\">"
                              "<p id=\"breadcrumb\">%s</p>"
                              "<p id=\"open-file\">"
                                "<a href='emacs:///%s'>Open file</a>"
                              "</p>"
                            "</div>"
                            "<h3>%s</h3>")
                    breadcrumb path it (org-get-heading)))))))))

Note that:

The org heading originally was placed at the flashcard "front", but after some other configuration I made, it miraculously no longer showed on the flashcard…

So I modified the field template above to include the <h3>(org-get-heading)</h3> to fetch the front of card myself.

Example AppleScript for emacs:// handler

Notice how in my field template I included an "Open file" link that links to the org file that the flashcard is created from, preceded by a emacs:// scheme. Below is an example of how you can make this work so that clicking on the link would open the file in emacs for MacOS.

on open location thisURL
    set thefile to (text 9 thru (count thisURL) of thisURL)
    do shell script "/usr/local/bin/emacsclient -c " & thefile
    return
end open location

You can save this as a .scpt, open with Script Editor, export -> as Application. Then configure the default app for emacs:// to point to the Application you just saved.

AppleScript is rather delightful isn't it (not /s)

Example CSS styling for anki card top section

Top section as in the "Title: Heading1 > Heading2" and "Open file" line.

Displays the entire meta in one line with breadcumb on left, "Open file" on right.

#meta {
  font-size: 1rem;
}
#breadcrumb {
  display: inline-block;
}
#open-file {
  display: inline-block;
  float: right;
}

Example CSS for internal org links within anki

If you're using org or org-roam links within flashcards to be synced with Anki, clicking on them would not be desirable when doing flashcards.

The below snippets selects all links other than the #meta section, and make them look muted, also unclickable.

a:not(#meta a) {
  color: initial;
  text-decoration: none;
  font-size: 1rem;
  pointer-events: none;
}

Note that

  • Setting color to initial allows anchor links to have the same color as normal flashcard text
  • font-size setting would make it smaller than normal text, and same size as the #meta section
  • The pointer-events setting makes it unclickable

Org latex

Installing dvisvgm

  • MacOS: Install MacTeX, or install through MacPorts/Homebrew
  • Ubuntu/Debian/Fedora/Gentoo: package manager
  • Windows: From website
  • NetBSD: dvisvgm is now available on pkgsrc! Thanks Thomas
  • Or use tlmgr to install dvisvgm after a texlive installation.
(setq org-latex-create-formula-image-program 'dvisvgm)
(plist-put org-format-latex-options :scale 4.0)
(plist-put org-format-latex-options :background nil)

TODO Scaling latex SVG overlays along with text

Unfixed issue :'/

Headings below are for my attempts to debug it…

The current status is basically, I've set the :scale to be 4.0 to make it satisfactorily readable when generating the preview for the first time. Using dvisvgm also allows it to be sharper. However I cannot find ANY method to scale the previews along with text, NOR scale up individual SVG previews.

I did try with Vanilla emacs too. No luck.

I have since given up and have decided to let this issue sit for prosperity.

For testing
(defun my/scale-current-overlay ()
  (interactive)
  (pcase major-mode
    ('latex-mode
     (dolist (ov (overlays-in (point-min) (point-max)))
       (if (eq (overlay-get ov 'category)
               'preview-overlay)
           (my/text-scale--resize-fragment ov))))
    ('org-mode
     (dolist (ov (overlays-in (- (point) 10) (+ (point) 5) ))
       (if (eq (overlay-get ov 'org-overlay-type)
               'org-latex-overlay)
           (my/text-scale--resize-fragment ov))))))

(defun my/text-scale--resize-fragment (ov)
  (overlay-put
   ov 'display
   (cons 'image
         (plist-put
          (cdr (overlay-get ov 'display))
          'margin 'right-margin)))
  (overlay-put
   ov 'display
   (cons 'image
         (plist-put
          (cdr (overlay-get ov 'display))
          :scale 7.0))))
\begin{equation} Hello \times world! \end{equation}

Here is an aleph $\lim\limits_{\aleph\to\infty}(blah) = BLAH$

$\lim\limits_{N\to\infty} (\sum\limits_{i=0}^N else \space if) = else$

list overlays at

Used this when I spent HOURS figuring out how to make the latex SVG preview overlays sync with text scaling

(defun list-overlays-at (&optional pos)
      "Describe overlays at POS or point."
      (interactive)
      (setq pos (or pos (point)))
      (let ((overlays (overlays-at pos))
            (obuf (current-buffer))
            (buf (get-buffer-create "*Overlays*"))
            (props '(priority window category face mouse-face display
                     help-echo modification-hooks insert-in-front-hooks
                     insert-behind-hooks invisible intangible
                     isearch-open-invisible isearch-open-invisible-temporary
                     before-string after-string evaporate local-map keymap
                     field))
            start end text)
        (if (not overlays)
            (message "None.")
          (set-buffer buf)
          (erase-buffer)
          (dolist (o overlays)
            (setq start (overlay-start o)
                  end (overlay-end o)
                  text (with-current-buffer obuf
                         (buffer-substring start end)))
            (when (> (- end start) 13)
              (setq text (concat (substring text 1 10) "...")))
            (insert (format "From %d to %d: \"%s\":\n" start end text))
            (dolist (p props)
              (when (overlay-get o p)
                (insert (format " %15S: %S\n" p (overlay-get o p))))))
          (pop-to-buffer buf))))
(not working) Solution from JDRiverRun

https://www.reddit.com/r/orgmode/comments/165zeuu/delighted_by_org_svg_preview/

not tangled as it doesn't work for me.

(defun my/resize-org-latex-overlays ()
  (cl-loop for o in (car (overlay-lists))
     if (eq (overlay-get o 'org-overlay-type) 'org-latex-overlay)
     do (plist-put (cdr (overlay-get o 'display))
		   :scale (expt text-scale-mode-step
				text-scale-mode-amount))))
(add-hook 'text-scale-mode-hook #'my/resize-org-latex-overlays nil t)
(not working) Solution from Karthinks

https://karthinks.com/software/scaling-latex-previews-in-emacs/

neither did this

(defun my/text-scale-adjust-latex-previews ()
  "Adjust the size of latex preview fragments when changing the
buffer's text scale."
  (pcase major-mode
    ('latex-mode
     (dolist (ov (overlays-in (point-min) (point-max)))
       (if (eq (overlay-get ov 'category)
               'preview-overlay)
           (my/text-scale--resize-fragment ov))))
    ('org-mode
     (dolist (ov (overlays-in (point-min) (point-max)))
       (if (eq (overlay-get ov 'org-overlay-type)
               'org-latex-overlay)
           (my/text-scale--resize-fragment ov))))))

(defun my/text-scale--resize-fragment (ov)
  (overlay-put
   ov 'display
   (cons 'image
         (plist-put
          (cdr (overlay-get ov 'display))
          :scale (+ 1.0 (* 0.25 text-scale-mode-amount))))))

Eshell

Significant portions of this section is credited to: https://github.com/howardabrams/hamacs/blob/main/ha-eshell.org

Opening files

(defun eshell-fn-on-files (fun1 fun2 args)
  "Call FUN1 on the first element in list, ARGS.
Call FUN2 on all the rest of the elements in ARGS."
  (unless (null args)
    (let ((filenames (flatten-list args)))
      (funcall fun1 (car filenames))
      (when (cdr filenames)
        (mapcar fun2 (cdr filenames))))
    ;; Return an empty string, as the return value from `fun1'
    ;; probably isn't helpful to display in the `eshell' window.
    ""))
(defun eshell/ff (&rest files)
  "find-file on first arg, find-file-other-window on rest"
  (eshell-fn-on-files 'find-file 'find-file-other-window files))

(defun eshell/f (&rest files)
  "Edit one or more files in another window."
  (eshell-fn-on-files 'find-file-other-window 'find-file-other-window files))

In case I somehow end up in (n)vi(m), I can possibly use my vim's <leader>q to quit, but still.

Oh yeah oopsie doopsie if I end up in nvim, since my leader there is SPC, same as doom emacs… Oh Noes!

(defalias 'eshell/emacs 'eshell/ff)
(defalias 'eshell/vi 'eshell/ff)
(defalias 'eshell/vim 'eshell/ff)
(defalias 'eshell/nv 'eshell/ff)
(defalias 'eshell/nvim 'eshell/ff)
(defun eshell/less (&rest files)
  "view-file-other-window"
  (view-file-other-window files))

(defalias 'eshell/more 'eshell/less)

Aliases

Some aliases >>> eshell-aliases-file

alias ll exa -lahg --git -t modified
alias clr clear 1
alias x exit
alias d dired $1

Kill window on exit https://stackoverflow.com/questions/51867693/emacs-eshell-kill-window-on-exit#51867960

(defun my/eshell-exit-with-window ()
  (when (not (one-window-p))
    (delete-window)))

(advice-add 'eshell-life-is-too-much :after 'my/eshell-exit-with-window)

Useful functions

(defun eshell/do (&rest args)
  "Execute a command sequence over a collection of file elements.
Separate the sequence and the elements with a `::' string.
For instance:

    do chown _ angela :: *.org(u'oscar')

The function substitutes the `_' sequence to a single filename
element, and if not specified, it appends the file name to the
command. So the following works as expected:

    do chmod a+x :: *.org"
  (seq-let (forms elements) (-split-on "::" args)
    (dolist (element (-flatten (-concat elements)))
      (message "Working on %s ... %s" element forms)
      (let* ((form (if (-contains? forms "_")
                       (-replace "_" element forms)
                     (-snoc forms element)))
             (cmd  (car form))
             (args (cdr form)))
        (eshell-named-command cmd args)))))

Clog up our M-x

(defun eshell--buffer-from-dir (dir)
  "Return buffer name of an Eshell based on DIR."
  (format "*eshell: %s*"
          (thread-first dir
                        (split-string "/" t)
                        (last)
                        (car))))

(defun eshell-there (parent)
  "Open an eshell session in a PARENT directory.
The window is smaller and named after this directory.
If an Eshell is already present that has been named
after PARENT, pop to that buffer instead."
  (if-let* ((term-name (eshell--buffer-from-dir parent))
            (buf-name  (seq-contains (buffer-list) term-name
                                     (lambda (a b) (string-equal (buffer-name b) a)))))
      (pop-to-buffer buf-name)

    (let* ((default-directory parent)
           (height (/ (window-total-height) 3)))
      (split-window-vertically (- height))
      (other-window 1)
      (setq eshell-buffer-name term-name)
      (eshell))))

(defun eshell-here ()
  "Opens a new shell in the directory of the current buffer.
Renames the eshell buffer to match that directory to allow more
than one eshell window."
  (interactive)
  (eshell-there (if (buffer-file-name)
                    (file-name-directory (buffer-file-name))
                  default-directory)))

(bind-key "C-`" 'eshell-here)

(defun ha-eshell-send (command &optional dir)
  "Send COMMAND to the Eshell buffer named with DIR.
  The Eshell may have moved away from the directory originally
  opened with DIR, but it should have the name of the buffer.
  See `eshell--buffer-from-dir'."
  (interactive "sCommand to Send: ")
  (unless dir
    (setq dir (projectile-project-root)))
  (save-window-excursion
    (eshell-there dir)
    (goto-char (point-max))
    (insert command)
    (eshell-send-input)))
(defun execute-command-on-file-buffer (cmd)
  "Executes a shell command, CMD, on the current buffer's file.
Appends the filename to the command if not specified, so:

    chmod a+x

Works as expected. We replace the special variable `$$' with the
filename of the buffer. Note that `eshell-command' executes this
command, so eshell modifiers are available, for instance:

    mv $$ $$(:r).txt

Will rename the current file to now have a .txt extension.
See `eshell-display-modifier-help' for details on that."
  (interactive "sExecute command on File Buffer: ")
  (let* ((file-name (buffer-file-name))
         (full-cmd (cond ((string-match (rx "$$") cmd)
                          (replace-regexp-in-string (rx "$$") file-name cmd))
                         ((and file-name (string-match (rx (literal file-name)) cmd))
                          cmd)
                         (t
                          (concat cmd " " file-name)))))
    (message "Executing: %s" full-cmd)
    (eshell-command full-cmd)))

Use package - eshell settings

(use-package! eshell
  ;;:type built-in
  ;;:custom (eshell-banner-message '(ha-eshell-banner))

  :init
  (setq eshell-error-if-no-glob t
        ;; This jumps back to the prompt:
        eshell-scroll-to-bottom-on-input 'all
        eshell-hist-ignoredups t
        eshell-save-history-on-exit t

        ;; Since eshell starts fast, let's dismiss it on exit:
        eshell-kill-on-exit t
        eshell-destroy-buffer-when-process-dies t

        ;; Can you remember the parameter differences between the
        ;; executables `chmod' and `find' and their Emacs counterpart?
        ;; Me neither, so this makes it act a bit more shell-like:
        eshell-prefer-lisp-functions nil))

EAT

(use-package eat
  :after eshell
  :bind
  (map! :map eat-mode-map "C-c C-d" #'eat-self-input)
  :hook (eshell-load . #'eat-eshell-mode))

LSP

Don't show errors & warnings at the side. Show them when my point is on it.

(use-package! lsp
  :config
  (setq lsp-ui-sideline-enable nil)
  (setq lsp-headerline-breadcrumb-enable t)
  (setq lsp-lens-enable t))

NOTE: Flycheck mode can be toggled in doom with SPC t f