~hedy/dotfiles

39b5aae9f52befd32476a92926a39d3a98b04cc4 — hedy 6 months ago 237637c
emacs: Track required *.el files

So the language breakdown of my dotfiles as a repo doesn't look like I
have that little elisp files :')

Regardless of my recent activity in the neovim space I still love emacs,
especially consult.el and org mode.
A .config/emacs/dotslash-modules/init-highlight.el => .config/emacs/dotslash-modules/init-highlight.el +99 -0
@@ 0,0 1,99 @@
;;; init-highlight.el --- Highlighting stuff
;;; Commentary:
;; Highlighting symbols, indentation, brackets, TODO/REVIEW/FIXME/HACK/XXX

;;; Code:

;; TODO: do I need hl-line package??

;; Highlight indentions
(use-package highlight-indent-guides
  :diminish
  :hook ((prog-mode yaml-mode) . highlight-indent-guides-mode)
  :init (setq highlight-indent-guides-method 'character
              highlight-indent-guides-responsive 'top
              highlight-indent-guides-suppress-auto-error t)
  :custom-face
  (highlight-indent-guides-character-face ((t (:foreground "dim gray"))))
  :config
  (with-no-warnings
    ;; Don't display first level of indentation
    (defun my-indent-guides-for-all-but-first-column (level responsive display)
      (unless (< level 1)
        (highlight-indent-guides--highlighter-default level responsive display)))
    (setq highlight-indent-guides-highlighter-function
          #'my-indent-guides-for-all-but-first-column)

    ;; Disable in `macrostep' expanding
    (with-eval-after-load 'macrostep
      (advice-add #'macrostep-expand
                  :after (lambda (&rest _)
                           (when highlight-indent-guides-mode
                             (highlight-indent-guides-mode -1))))
      (advice-add #'macrostep-collapse
                  :after (lambda (&rest _)
                           (when (derived-mode-p 'prog-mode 'yaml-mode)
                             (highlight-indent-guides-mode 1)))))

    ;; Don't display indentations in `swiper'
    ;; https://github.com/DarthFennec/highlight-indent-guides/issues/40
    (with-eval-after-load 'ivy
      (defun my-ivy-cleanup-indentation (str)
        "Clean up indentation highlighting in ivy minibuffer."
        (let ((pos 0)
              (next 0)
              (limit (length str))
              (prop 'highlight-indent-guides-prop))
          (while (and pos next)
            (setq next (text-property-not-all pos limit prop nil str))
            (when next
              (setq pos (text-property-any next limit prop nil str))
              (ignore-errors
                (remove-text-properties next pos '(display nil face nil) str))))))
      (advice-add #'ivy-cleanup-string :after #'my-ivy-cleanup-indentation))))


;; Colorize color names in buffers
(use-package rainbow-mode
  :diminish
  :bind (:map special-mode-map
         ("w" . rainbow-mode))
  :hook ((html-mode php-mode) . rainbow-mode)
  :config
  (with-no-warnings
    ;; HACK: Use overlay instead of text properties to override `hl-line' faces.
    ;; @see https://emacs.stackexchange.com/questions/36420
    (defun my-rainbow-colorize-match (color &optional match)
      (let* ((match (or match 0))
             (ov (make-overlay (match-beginning match) (match-end match))))
        (overlay-put ov 'ovrainbow t)
        (overlay-put ov 'face `((:foreground ,(if (> 0.5 (rainbow-x-color-luminance color))
                                                  "white" "black"))
                                (:background ,color)))))
    (advice-add #'rainbow-colorize-match :override #'my-rainbow-colorize-match)

    (defun my-rainbow-clear-overlays ()
      "Clear all rainbow overlays."
      (remove-overlays (point-min) (point-max) 'ovrainbow t))
    (advice-add #'rainbow-turn-off :after #'my-rainbow-clear-overlays)))

;; Highlight brackets according to their depth
(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))

;; Highlight TODO and similar keywords in comments and strings
(use-package hl-todo
  :demand t
  :config
  (global-hl-todo-mode 1)
  (dolist (keyword '("REVIEW" "HACK"))
    (cl-pushnew `(,keyword . ,(face-foreground 'warning)) hl-todo-keyword-faces))
  :bind (:map hl-todo-mode-map
         ([C-f3] . hl-todo-occur)
         ("C-c t p" . hl-todo-previous)
         ("C-c t n" . hl-todo-next)
         ("C-c t o" . hl-todo-occur))
  ;;:hook (after-init . global-hl-todo-mode)
)
 (provide 'init-highlight)
;;; init-highlight.el ends here

A .config/emacs/dotslash-modules/packages.el => .config/emacs/dotslash-modules/packages.el +778 -0
@@ 0,0 1,778 @@
;; NOTE:
;; This file is generated from packages.org

(defconst IS-MAC      (eq system-type 'darwin))
(defconst IS-LINUX    (memq system-type '(gnu gnu/linux gnu/kfreebsd berkeley-unix)))
(defconst IS-WINDOWS  (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD      (memq system-type '(darwin berkeley-unix gnu/kfreebsd)))
(defconst HAS-EMACS28+    (> emacs-major-version 27))
(defconst HAS-EMACS29+    (> emacs-major-version 28))
(defconst HAS-DYNMODULES  (featurep 'dynamic-modules))
(defconst HAS-NATIVECOMP  (featurep 'native-compile))

(set-face-attribute 'completions-first-difference nil :weight 'normal)

(defvar ./corfu-bar-color "gray20" ;; Default value is for dark themes
  "Color for the corfu scroll bar.")
(defvar ./cursor-color "white"
  "Color for cursor")
(defvar ./theme-type "dark"
  "Dark or light")

(use-package ef-themes
  :config
  (load-theme 'ef-elea-dark t)
  (set-face-attribute 'cursor nil :background "white")
  (set-face-attribute 'font-lock-builtin-face nil :weight 'normal)
  (set-face-attribute 'font-lock-keyword-face nil :weight 'normal)
  (set-face-attribute 'font-lock-variable-name-face nil :foreground "white")
  (set-face-attribute 'font-lock-property-name-face nil :foreground "white")
  (set-face-attribute 'font-lock-constant-face nil :foreground "white")

  (set-face-attribute 'window-divider nil :foreground "gray20")
  (set-face-attribute 'window-divider-first-pixel nil :foreground "black")
  (set-face-attribute 'window-divider-last-pixel nil :foreground "black")
)

(setq ./theme-type (symbol-name (frame-parameter nil 'background-mode)))
(setq ./cursor-color (if (string= ./theme-type "dark") "white" "black"))
(set-face-attribute 'cursor nil :background ./cursor-color)
;; TODO: Do this for window divider and corfu UI items, and magit diff backgrounds.
;; and org-link

(setq tab-bar-auto-width-max nil)

(setq tab-bar-new-tab-choice "*scratch*")

;; FIXME: Doesn't work
(set-face-attribute 'tab-bar-tab-inactive nil :background "textBackgroundColor")

(use-package evil
  :demand t
  :init
  (setq evil-search-module 'evil-search
        evil-ex-search-vim-style-regexp t)
  (setq evil-ex-visual-char-range t  ; column range for ex commands
        evil-mode-line-format 'nil
        ;; more vim-like behavior
        evil-symbol-word-search t)
  (setq evil-default-cursor 'box
        evil-normal-state-cursor 'box
        evil-emacs-state-cursor  'bar
        evil-insert-state-cursor 'bar
        evil-visual-state-cursor 'hollow)
        ;; Only do highlighting in selected window so that Emacs has less work
        ;; to do highlighting them all.
  (setq evil-ex-interactive-search-highlight 'selected-window
        ;; It's infuriating that innocuous "beginning of line" or "end of line"
        ;; errors will abort macros, so suppress them:
        evil-kbd-macro-suppress-motion-error t)
  (setq evil-want-C-u-scroll t) ; must be set in :init
  ;; REVIEW: Is this needed if evil-redo-function is set?
  (setq evil-undo-system 'undo-redo)  ;; 'undo-redo from Emacs 28
  (setq evil-undo-function 'undo)
  (setq evil-redo-function 'undo-redo)

  :config
  (setq evil-visual-update-x-selection-p nil)
  (setq evil-shift-width 2)
  (setq evil-cross-lines t)

  ;; Emacs keybindings in evil insert state - must be set in :config
  (setq evil-insert-state-map (make-sparse-keymap))
  (define-key evil-insert-state-map (kbd "<escape>") 'evil-normal-state)
  (define-key evil-emacs-state-map (kbd "<escape>") 'evil-normal-state)
  (define-key evil-insert-state-map (kbd "S-C-e") 'evil-scroll-line-down)
  (define-key evil-emacs-state-map (kbd "S-C-e") 'evil-scroll-line-down)
  (define-key evil-insert-state-map (kbd "S-C-y") 'evil-scroll-line-up)
  (define-key evil-emacs-state-map (kbd "S-C-y") 'evil-scroll-line-up)
  ;; Don't put vim yanks into system clipboard
  ;; But use shift C-v / C-c to paste/copy from system clipboard instead
  (setq select-enable-clipboard nil)
  (global-set-key (kbd "S-C-c") #'clipboard-kill-ring-save)
  (global-set-key (kbd "S-C-v") #'clipboard-yank)
  
  (global-set-key (kbd "<escape>") 'keyboard-escape-quit)
  ;; FIXME
  (evil-set-leader nil (kbd "S-C-SPC")) ;; C-SPC is mark set
  (evil-set-leader 'normal (kbd "SPC"))
  
  (define-key evil-command-line-map "\C-a" 'move-beginning-of-line)
  (define-key evil-command-line-map "\C-e" 'move-end-of-line)
  ;; (define-key evil-command-line-map "\C-d" nil t)
  ;; (define-key evil-command-line-map "\C-l" nil t)
  
  (global-set-key (kbd "s-<left>") #'previous-buffer)
  (global-set-key (kbd "s-<right>") #'next-buffer)
  (global-set-key (kbd "S-s-<down>") #'evil-window-down)
  (global-set-key (kbd "S-s-<up>") #'evil-window-up)
  (global-set-key (kbd "S-s-<left>") #'evil-window-left)
  (global-set-key (kbd "S-s-<right>") #'evil-window-right)
  (defvar ./leader-map (make-sparse-keymap)
    "Keymap for leader shortcuts")
  (define-key evil-normal-state-map (kbd "SPC") ./leader-map)
  (define-key ./leader-map "q" #'evil-quit)
  (define-key ./leader-map "w" #'save-buffer)
  (define-key ./leader-map "x" #'evil-save-modified-and-close)
  (define-key ./leader-map "1" #'delete-other-windows)
  
  (defvar ./leader-buffer-map (make-sparse-keymap)
    "Keymap for leader shortcuts for buffers")
  (define-key ./leader-map "b" ./leader-buffer-map)
  (define-key ./leader-buffer-map "b" #'consult-buffer)
  (define-key ./leader-buffer-map "s" #'save-buffer)
  (define-key ./leader-buffer-map "d" #'evil-delete-buffer)
  
  (defvar ./leader-file-map (make-sparse-keymap)
    "Keymap for leader shortcuts for files")
  (define-key ./leader-map "f" ./leader-file-map)
  (define-key ./leader-file-map "f" #'find-file)
  (define-key ./leader-file-map "r" #'consult-recent-file)
  (define-key ./leader-file-map "p" #'project-find-file)
  
  (defvar ./leader-frame-map (make-sparse-keymap)
    "Keymap for leader shortcuts for frames")
  (define-key ./leader-map "F" ./leader-frame-map)
  (define-key ./leader-frame-map "q" #'delete-frame)
  (define-key ./leader-frame-map "d" #'delete-frame)
  (define-key ./leader-frame-map "u" #'undelete-frame)
  (define-key ./leader-frame-map "R" #'rename-frame)
  (define-key ./leader-frame-map "o" #'other-frame)
  (define-key ./leader-frame-map "c" #'clone-frame)

  (defun ./evil-off ()
    "Call `turn-off-evil-mode' and show a message"
    (interactive)
    (turn-off-evil-mode)
    (message "Evil mode is turned off"))

  ;; Disable evil in some modes
  (dolist (mode '(elpaca-ui-mode
                  dired-mode
                  magit-mode))
    (evil-set-initial-state mode 'emacs))

  (evil-mode 1)
  )

(use-package elpher)
;; eww is part of emacs now?!!
;;(use-package eww)

(use-package visual-fill-column
  :init
  (setq-default visual-fill-column-center-text t))

(use-package imenu-list
  :config
  (setq imenu-list-auto-resize t)
  ;; Auto-update Ilist buffer
  :hook (imenu-list-major-mode . (lambda ()
                                   (imenu-list-minor-mode 1)
                                   (visual-line-mode 1)  ;; REVIEW
                                   (display-line-numbers-mode -1)
                                   (evil-insert-state 1))))

(use-package math-symbol-lists
  :after cape
  :config
  ;; 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))
  )

;; (use-package dired
  ;; :elpaca nil
  ;; :config
  (setq delete-by-moving-to-trash t
        ;; Emacs 29
        dired-make-directory-clickable t
        dired-mouse-drag-files t
        )

  ;;:hook
  ;; FIXME: replicate evil state cursor style switching here
  (defun ./cursor-toggle-readonly ()
    (if buffer-read-only
        (progn
          (setq cursor-type 'box)
          (set-cursor-color "orange"))
      (setq cursor-type 'bar)
      (set-cursor-color ./cursor-color)))

  (add-hook 'read-only-mode-hook #'./cursor-toggle-readonly)
  (add-hook 'dired-mode-hook (lambda () (hl-line-mode) (./cursor-toggle-readonly)))
  ;; )

(add-hook 'minibuffer-setup-hook (lambda () (setq cursor-type 'bar)))
(add-hook 'minibuffer-exit-hook (lambda () (setq cursor-type 'box)))

(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))))
  :hook
  ((org-mode markdown-mode) . wrap-region-mode)
)

(use-package magit)

(use-package breadcrumb
  :diminish breadcrumb-mode
  :init
  (breadcrumb-mode 1))

(use-package vertico
  :init
  (vertico-mode)
  ;; Grow and shrink the Vertico minibuffer
  (setq vertico-resize t)
  ;; Optionally enable cycling for `vertico-next' and `vertico-previous'.
  (setq vertico-cycle t)
  :hook
  ;; For find-file, remove old file path if I start typing a new one
  ('rfn-eshadow-update-overlay-hook . #'vertico-directory-tidy)
  )

(use-package orderless
  :init
  (setq completion-styles '(orderless)
        completion-category-defaults nil
        completion-category-overrides '((file (styles partial-completion)))))
;; Persist history over Emacs restarts. Vertico sorts by history position.
(use-package savehist
  :elpaca nil
  :init
  (savehist-mode))
;; Pasted from vertico
(use-package emacs
  :elpaca nil
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; Alternatively try `consult-completing-read-multiple'.
  (defun crm-indicator (args)
    (cons (concat "[CRM] " (car args)) (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)
  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
  ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
  ;; Vertico commands are hidden in normal buffers.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)
  ;; Enable recursive minibuffers
  (setq enable-recursive-minibuffers t)
  ;; From corfu
  ;; TAB cycle if there are only few candidates
  (setq completion-cycle-threshold 3)

  ;; Emacs 28: Hide commands in M-x which do not apply to the current mode.
  ;; Corfu commands are hidden, since they are not supposed to be used via M-x.
  ;; (setq read-extended-command-predicate
  ;;       #'command-completion-default-include-p)
)

(use-package marginalia
  :diminish
  :config
  (setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
  (marginalia-mode 1)
)

(use-package consult
  :config
  (global-set-key (kbd "C-s") 'consult-line)
  (define-key evil-insert-state-map (kbd "C-s") 'isearch-forward)
  (define-key evil-emacs-state-map (kbd "C-s") 'isearch-forward)
  (global-set-key (kbd "C-c g") 'consult-org-heading)
  (global-set-key (kbd "C-x C-b") 'consult-buffer)
  ;; Doesn't work?
  (global-set-key [?\C-\t] 'consult-buffer)
  (define-key minibuffer-local-map (kbd "C-r") 'consult-history)

  (setq completion-in-region-function #'consult-completion-in-region)
  )

(use-package corfu
  :custom
  (corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
  ;; Default is M-SPC, if M-SPC is bound like I have on my Mac (Alfred) S-M-SPC also works
  ;;(corfu-separator ?\s) ;; Orderless separator
  ;; separator: Quit at boundary if no `corfu-separator' inserted
  (corfu-quit-at-boundary 'separator)
  ;; separator: only stay alive if no match and `corfu-separator' inserted
  (corfu-quit-no-match 'separator)
  ;; Don't change what I typed to what I selected when previewing completions
  (corfu-preview-current nil)
  (corfu-preselect 'first)
  ;; Default = #'insert. Options: quit, nil
  ;;(corfu-on-exact-match nil)
  ;; Prevent last/first item being hidden behind windows
  ;; FIXME: Doesn't work
  (corfu-scroll-margin 2)
  (corfu-right-margin-width 2)

  ;; Enable Corfu only for certain modes.
  ;; :hook ((prog-mode . corfu-mode)
  ;;        (shell-mode . corfu-mode)
  ;;        (eshell-mode . corfu-mode))

  ;; FIXME: doesn't work: evil insert/emacs keybinds takes higher precendence it seems
  (define-key corfu-map (kbd "<escape>") 'corfu-quit)

  :custom-face
  (corfu-border ((t (:background "gray20" :weight bold))))
  (corfu-default ((t (:inherit fixed-pitch))))

  :init
  ;; Recommended: Enable Corfu globally.
  ;; This is recommended since Dabbrev can be used globally (M-/).
  ;; See also `global-corfu-modes'.
  (global-corfu-mode)
  (corfu-popupinfo-mode 1)

  :config
  (setq corfu-bar-width 0.8)
  (set-face-attribute 'corfu-bar nil :background ./corfu-bar-color)
  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input)
                (eq (current-local-map) read-passwd-map))
      ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
      (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
                  corfu-popupinfo-delay '(0 . 0)) ;; Use popupinfo in minibuffer too, why not?
      (corfu-mode 1)))
  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1)

  (setq corfu-popupinfo-delay '(0 . 0))
)

(use-package kind-icon
  :after corfu
  :custom
  (kind-icon-mapping ;; These are fetched (and cached) from pictogrammers.com/library/mdi
    '((array "ar" :icon "code-brackets" :face font-lock-type-face)
      (boolean "b" :face font-lock-builtin-face)
      (class "C" :face font-lock-type-face) ;; family-tree could be used. but too dense
      (color "#" :icon "palette" :face success)
      (command ">_" :face default)
      (constant "cn" :icon "lock-remove-outline" :face font-lock-constant-face)
      (constructor "C+" :icon "plus-circle-multiple" :face font-lock-function-name-face)
      (enummember "em" :icon "order-bool-ascending-variant" :face font-lock-builtin-face)
      (enum-member "em" :icon "order-bool-ascending-variant" :face font-lock-builtin-face)
      (enum "e" :icon "format-list-bulleted-square" :face font-lock-builtin-face)
      (event "ev" :icon "lightning-bolt-outline" :face font-lock-warning-face)
      (field "fd" :face font-lock-variable-name-face)
      (file "F" :icon "file-document-outline" :face font-lock-string-face)
      (folder "D" :icon "folder" :face font-lock-doc-face)
      (interface "if" :icon "application-brackets-outline" :face font-lock-type-face)
      (keyword "kw" :face font-lock-keyword-face)
      (macro "mc" :icon "lambda" :face font-lock-keyword-face)
      (magic "ma" :icon "shimmer" :face font-lock-builtin-face)
      (method "me" :face font-lock-function-name-face)
      (function "f" :icon "function" :face font-lock-function-name-face)
      (module "mo" :icon "package-variant-closed" :face font-lock-preprocessor-face)
      (numeric "0" :icon "numeric" :face font-lock-builtin-face)
      (operator "÷" :icon "division" :face font-lock-comment-delimiter-face)
      (param "pa" :icon "cog-outline" :face default)
      (property "pr" :icon "wrench" :face font-lock-variable-name-face)
      (reference "rf" :icon "library" :face font-lock-variable-name-face)
      (snippet "S" :face font-lock-string-face)
      (string "\"" :icon "text-box" :face font-lock-string-face)
      (struct "{}" :icon "code-braces" :face font-lock-variable-name-face)
      (text " " :face font-lock-doc-face) ; text-short could be used
      (typeparameter "tp" :icon "format-list-bulleted-type" :face font-lock-type-face)
      (type-parameter "tp" :icon "format-list-bulleted-type" :face font-lock-type-face)
      (unit "u" :icon "square-rounded-outline" :face font-lock-constant-face)
      (value "vl" :icon "plus-circle-outline" :face font-lock-builtin-face)
      (variable "v" :face font-lock-variable-name-face)
      (t "?" :face font-lock-warning-face)))
    (kind-icon-blend-background nil)
  :custom-face
  (kind-icon-default-face ((t (:background nil))))

  :config
  ;;(setq kind-icon-default-face 'corfu-default) ; to compute blended backgrounds correctly
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

(use-package cape
  ;;:after math-symbol-lists
  :config
  ;; Add useful defaults completion sources from cape
  ;; (add-to-list 'completion-at-point-functions #'cape-file)
  ;; ;;(add-to-list 'completion-at-point-functions #'cape-dabbrev)
  ;;(add-to-list 'completion-at-point-functions #'cape-tex)
  (add-to-list 'completion-at-point-functions #'cape-emoji)

  ;; Silence the pcomplete capf, no errors or messages!
  ;; Important for corfu
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)

  ;; Ensure that pcomplete does not write to the buffer
  ;; and behaves as a pure `completion-at-point-function'.
  (advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify)

  (define-key evil-insert-state-map (kbd "C-x C-f") 'cape-file)
  (define-key evil-insert-state-map (kbd "C-x C-d") 'cape-dict)
  (define-key evil-insert-state-map (kbd "C-x C-w") 'cape-dabbrev)
  (define-key evil-insert-state-map (kbd "C-x C-:") 'cape-emoji)

  (cape-char--define math "math" ?\\)
  (add-to-list 'completion-at-point-functions #'cape-math)
  (define-key evil-insert-state-map (kbd "C-x C-$") 'cape-math)

  :hook (eshell-mode-hook . (lambda () (setq-local corfu-quit-at-boundary t
                                                   corfu-quit-no-match t
                                                   corfu-auto nil)
                              (corfu-mode)))

)

(use-package corfu-candidate-overlay
  :config
  (corfu-candidate-overlay-mode 1) ;; This is global
  (set-face-attribute 'corfu-candidate-overlay-face nil :foreground "dim grey")
  ;; Use TAB to accept a completion, how cool is that!
  (defun ./insert-state-tab ()
    "Handle TAB key in insert state.
  
  If corfu-candidate-overlay's overlay is active, calls
  `corfu-candidate-overlay--get-overlay-property', otherwise
  `evil-toggle-fold'. See my packages.org for this section for why I
  didn't use `org-cycle' here."
    (interactive)
    (if (overlayp corfu-candidate-overlay--overlay)
        (progn
          ;; This check is taken exactly from the implementation of
          ;; `corfu-candidate-overlay-complete-at-point's (as of
          ;; writing).
          (corfu-candidate-overlay--show)
          (if (and (overlayp corfu-candidate-overlay--overlay)
                   (not (string= (corfu-candidate-overlay--get-overlay-property 'after-string) "")))
              (corfu-candidate-overlay-complete-at-point)
            (if (string-match-p
                 "^\*+ "
                 (buffer-substring-no-properties
                  (line-beginning-position)
                  (line-end-position)))
            (evil-toggle-fold)
            (indent)
                )
            ))
            (if (string-match-p
                 "^\*+ "
                 (buffer-substring-no-properties
                  (line-beginning-position)
                  (line-end-position)))
            (evil-toggle-fold)
            (indent-for-tab-command)
      )))
  (define-key evil-insert-state-map (kbd "TAB") './insert-state-tab)
)

(use-package which-key
  :diminish
  :config
  (which-key-setup-side-window-right)
  (which-key-mode 1))

(use-package org
  :elpaca nil
  :config
  (setq org-startup-indented t)
  (setq org-edit-src-content-indentation 0)
  (setq org-list-indent-offset 2)

  ;; (dolist (item '( ;; Newline used to add prefix to tangled file
  ;;                '(org-block ((t (:inherit fixed-pitch))))
  ;;                '(org-code ((t (:inherit (shadow fixed-pitch)))))
  ;;                '(org-document-info ((t (:foreground "dark orange"))))
  ;;                '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
  ;;                '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
  ;;                '(org-link ((t (:foreground "deep sky blue" :underline t))))
  ;;                '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  ;;                '(org-property-value ((t (:inherit fixed-pitch))) t)
  ;;                '(org-block-begin-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
  ;;                '(org-block-end-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
  ;;                '(org-drawer ((t (:inherit fixed-pitch))) t)
  ;;                '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
  ;;                '(org-table ((t (:inherit fixed-pitch))))
  ;;                '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
  ;;                '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))))
  ;;   (apply #'set-face-attribute item))
  (custom-theme-set-faces
   'user
   '(org-block ((t (:inherit fixed-pitch))))
   '(org-code ((t (:inherit (shadow fixed-pitch)))))
   '(org-document-info ((t (:foreground "dark orange"))))
   '(org-document-info-keyword ((t (:inherit (shadow fixed-pitch)))))
   '(org-indent ((t (:inherit (org-hide fixed-pitch)))))
   '(org-link ((t (:foreground "deep sky blue" :underline t))))
   '(org-meta-line ((t (:inherit (font-lock-comment-face fixed-pitch)))))
   '(org-property-value ((t (:inherit fixed-pitch))) t)
   '(org-block-begin-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
   '(org-block-end-line ((t (:inherit (font-lock-comment-face fixed-pitch)))) t)
   '(org-drawer ((t (:inherit fixed-pitch))) t)
   '(org-special-keyword ((t (:inherit (font-lock-comment-face fixed-pitch)))))
   '(org-table ((t (:inherit fixed-pitch))))
   '(org-tag ((t (:inherit (shadow fixed-pitch) :weight bold :height 0.8))))
   '(org-verbatim ((t (:inherit (shadow fixed-pitch)))))
   )

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)
     (lua . t)
     (js . t)))

  (defun org-babel-execute:nim (body params)
    "Execute a block of Nim code with org-babel."
    (let ((in-file (org-babel-temp-file "n" ".nim"))
          (verbosity (or (cdr (assq :verbosity params)) 0)))
      (with-temp-file in-file
        (insert body))
      (org-babel-eval
       (format "nim compile --verbosity=%d --run %s" verbosity
               (org-babel-process-file-name in-file))
       "")))

  (defun org-babel-execute:moonscript (body params)
    "Execute a block of MoonScript code with org-babel."
    (let ((in-file (org-babel-temp-file "m" ".moon")))
      (with-temp-file in-file
        (insert body))
      (org-babel-eval
       (format "moon %s" (org-babel-process-file-name in-file)) "")))

  :hook
  (org-mode . (lambda () (visual-line-mode 1)
                (variable-pitch-mode)
                (display-line-numbers-mode -1)))
  )

(use-package org-superstar
  :config
  (setq org-superstar-configure-like-org-bullets t)
  :hook
  (org-mode . (lambda () (org-superstar-mode 1))))

(use-package org-auto-tangle
  :defer t
  :hook (org-mode . org-auto-tangle-mode)
  :config
  (setq org-auto-tangle-babel-safelist '(
                                         "~/.config/emacs/packages.org"
                                         "~/.config/emacs/init.org")))

(use-package lua-mode)
(use-package moonscript)
(use-package nim-mode)

(use-package eglot
  :elpaca nil
  :defer t
  :hook
  ((python-ts-mode go-ts-mode lua-mode) . eglot-ensure)
)

;; Open python files in tree-sitter mode.
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode))
(add-to-list 'auto-mode-alist
             '("\\.go\\'" . (lambda ()
                               (go-ts-mode)
                               )))
(add-to-list 'auto-mode-alist
             '("go.mod\\'" . (lambda ()
                               (go-mod-ts-mode)
                               )))

;; FIXME
(use-package diminish
  :config
  (diminish 'buffer-face-mode)
  (diminish 'org-auto-tangle-mode)
  (diminish 'eldoc-mode)
  (diminish 'auto-revert-mode)
  (diminish 'visual-line-mode)
  (diminish 'org-indent-mode)
  (diminish 'subword-mode)
  )

(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))

(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)

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

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

(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)))))

(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 ./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
  :elpaca nil
  :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

        ;; Parameter differences could be hard to remember. Maybe next time
        eshell-prefer-lisp-functions nil))

(use-package eat
  :config
  (define-key eat-mode-map (kbd "C-c C-d") #'eat-self-input)
  ;; :hook
  ;; (eshell-mode . #'eat-eshell-mode)
  )

(provide 'packages)

A .config/emacs/init.el => .config/emacs/init.el +287 -0
@@ 0,0 1,287 @@
;; NOTE:
;; This file is generated from init.org

(defvar ./variable-pitch-display-font
  "Fira Sans"
  "Font used for titles")

(defvar ./face-fixed-pitch
  '(:foundry "apple" :family "Fira Code" :height 160)
  "Contents to be passed to (`set-face-attribute' 'fixed-pitch nil [...]) ")
(defvar ./face-variable-pitch
  '(:foundry "apple" :family "Inter" :height 170)
  "Contents to be passed to (`set-face-attribute' 'variable-pitch nil [...]) ")

(load (locate-user-emacs-file "dotslash-local-pre.el") :no-error :no-message)

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(defun display-startup-echo-area-message ()
  (message (format "Emacs %s loaded from %s in %s"
            emacs-version user-emacs-directory (emacs-init-time))))

(setq initial-scratch-message
      (concat ";; *SCRATCH*\n"
              ";; C-c C-b (eval-buffer)  C-c C-r (eval-region)\n"
              ";; C-x C-s will let you choose where to save\n\n; "))

;; Setting this in early-init doesn't seem to work
(set-frame-position (selected-frame) 100 10)
(set-face-attribute 'default nil :height 160)

(add-to-list 'bdf-directory-list "~/Library/Fonts")

;; (custom-theme-set-faces
;;  'user
;;  '(variable-pitch ((t (:inherit ./face-variable-pitch))))
;;  '(fixed-pitch ((t (:inherit ./face-fixed-pitch)))))

(apply #'set-face-attribute 'variable-pitch nil ./face-variable-pitch)
(apply #'set-face-attribute 'fixed-pitch nil ./face-fixed-pitch)
(apply #'set-face-attribute 'default nil ./face-fixed-pitch)

;;(global-display-line-numbers-mode 1)
(add-hook 'prog-mode-hook (lambda () (display-line-numbers-mode 1)))
(add-hook 'text-mode-hook (lambda () (display-line-numbers-mode 1)))

;; I think this saves the last cursor pos or something
;; FIXME: doesn't work
(require 'saveplace)
(setq-default save-place t)
(setq save-place-file (expand-file-name ".places" user-emacs-directory))

(setq make-backup-files nil)
(setq auto-save-default nil)
(setq-default major-mode 'text-mode)
(setq sentence-end-double-space nil)

;; Always tabs except for *.go?
;; TODO: set up language mode packages
(setq-default c-basic-offset  4
              tab-width       4
              indent-tabs-mode nil)
;; Some modes
(recentf-mode 1)
(show-paren-mode 1)
(setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit)
(electric-pair-mode 1)
(winner-mode 1)
(delete-selection-mode 1) ;; Select and type would replace the selected text

;; IDK
(add-hook 'prog-mode-hook #'subword-mode)
(add-hook 'minibuffer-setup-hook #'subword-mode)

(add-hook 'elisp-mode-hook
          (lambda ()
            ;; These are never used...
            ;; (local-set-key (kbd "C-c C-x") #'ielm)
            ;; (local-set-key (kbd "C-c C-c") #'eval-defun)
            (local-set-key (kbd "C-c C-r") 'eval-region)
            (local-set-key (kbd "C-c C-b") #'eval-buffer)))

(setq window-divider-default-right-width 8)
(setq window-divider-default-places 'right-only)
(window-divider-mode 1)

(defun ./org-insert-src (beg end)
  "Insert (or wrap region with, or cancel) src block.

If cursor currently on an empty line, it is wrapped in src block, with
no other content in the src block other than the empty line, then the
src block together with the line after it (if empty) is deleted. This
undoes the effect of ./org-insert-src without active region, and
cancelling org-edit-src with C-c C-k.

Code type is prompted and `org-edit-src-code' called only for insert,
not for wrap.

Uses 'elisp' if currently in user-emacs-directory."
  (interactive (if (use-region-p)
                   (list (region-beginning) (region-end))
                 (list (point-min) (point-min))))
  (let ((selection (buffer-substring-no-properties beg end))
        (code-type '(if (string=
                         (file-truename user-emacs-directory)
                         (file-name-directory (buffer-file-name)))
                        "elisp"
                      (read-from-minibuffer "#+BEGIN_SRC "))))
    (if (< (length selection) 2)
        (if (./org-empty-src-p)
            ;; Delete empty src block and exit
            (progn
              (previous-line)
              (delete-line) ;; Newline also deleted
              (delete-line)
              (delete-line)
              ;; Delete empty line inserted by ./org-insert-src itself
              (if (./match-line-p "")
                  (delete-line))) 
          ;; Otherwise:
          ;; Insert src block with given code type and edit
          (progn
            (setq code-type (eval code-type))
            (deactivate-mark)
            (beginning-of-line)
            (newline)
            (insert (format "#+BEGIN_SRC %s\n" code-type))
            (newline)
            (insert "#+END_SRC\n")
            (previous-line 2)
            (org-edit-src-code)))
      ;; Wrap selected region
      ;;(setq code-type (eval code-type))
      (goto-char beg)
      (previous-line) (end-of-line)
      (newline)
      (insert "#+BEGIN_SRC ")
      ;; save-excursion doesn't seem to work here
      (goto-char (+ end 11)) (end-of-line)
      (newline)
      (insert "#+END_SRC")
      ;; FIXME: putting cursor at the begin src part afterwards doesn't work
      (re-search-backward "^\\s-*#\\+BEGIN_SRC")
      (end-of-line))))

(defun ./match-line-p (regexp &optional move keep-pos start)
  "Wrapper around string-match-p to use contents of current line.

Returns whether current line, after moving down by MOVE lines, can be
matched with REGEXP.

If REGEXP is an empty string, return t for empty line, nil otherwise.
MOVE argument is passed to `next-line'.

If REGEXP and is non-nil, REGEXP and START is passed to
`string-match-p' with no changes, and its return-value is returned
as-is.

MOVE argument is passed as-is to `next-line' immediately.

If KEEP-POS is non-nil, pass MOVE argument to `previous-line' after obtaining
contents of the required line."
  (next-line move)
  (let ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
    (if keep-pos
        (previous-line move))
    (if (= (length regexp) 0)
        (if (= (length line) 0)
            t nil)
      (string-match-p regexp line start))))

(defun ./org-empty-src-p ()
  "Return whether point is within a org src block with a single empty line."
  (let ((line))
    (save-excursion
      (if (./match-line-p "^\\s-*#\\+begin_src" -1)
          (if (./match-line-p "" +1)
              (if (./match-line-p "^\\s-*#\\+end_src" +1)
                  t nil) nil) nil))))

(defun ./org-split-src ()
  "Split current src block into two blocks at point.

Retains src properties."
  (interactive)
  (insert "#+END_SRC\n\n")
  (save-excursion
    (re-search-backward "^\\s-*#\\+begin_src")
    (defvar beginsrc (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
  (insert beginsrc)
  (previous-line))

(defun ./load-directory (dir)
  "Load *.el files in a given directory"
  (let ((load-it (lambda (f)
                   (load-file (concat (file-name-as-directory dir) f)))))
    (mapc load-it (directory-files dir nil "\\.el$"))))

(defun ./load-file-if-exists (file)
  "Same as load-file but NOP if file does not exist"
  (if (file-exists-p file)
      (load-file file)))

(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))))))

(defvar elpaca-installer-version 0.5)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
                              :ref nil
                              :files (:defaults (:exclude "extensions"))
                              :build (:not elpaca--activate-package)))
(let* ((repo  (expand-file-name "elpaca/" elpaca-repos-directory))
       (build (expand-file-name "elpaca/" elpaca-builds-directory))
       (order (cdr elpaca-order))
       (default-directory repo))
  (add-to-list 'load-path (if (file-exists-p build) build repo))
  (unless (file-exists-p repo)
    (make-directory repo t)
    (when (< emacs-major-version 28) (require 'subr-x))
    (condition-case-unless-debug err
        (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                 ((zerop (call-process "git" nil buffer t "clone"
                                       (plist-get order :repo) repo)))
                 ((zerop (call-process "git" nil buffer t "checkout"
                                       (or (plist-get order :ref) "--"))))
                 (emacs (concat invocation-directory invocation-name))
                 ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
                                       "--eval" "(byte-recompile-directory \".\" 0 'force)")))
                 ((require 'elpaca))
                 ((elpaca-generate-autoloads "elpaca" repo)))
            (progn (message "%s" (buffer-string)) (kill-buffer buffer))
          (error "%s" (with-current-buffer buffer (buffer-string))))
      ((error) (warn "%s" err) (delete-directory repo 'recursive))))
  (unless (require 'elpaca-autoloads nil t)
    (require 'elpaca)
    (elpaca-generate-autoloads "elpaca" repo)
    (load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))

;; Install use-package support
(elpaca elpaca-use-package
  ;; Enable :elpaca use-package keyword.
  (elpaca-use-package-mode)
  ;; Assume :elpaca t unless otherwise specified.
  (setq elpaca-use-package-by-default t))

;; Block until current queue processed.
(elpaca-wait)

;;When installing a package which modifies a form used at the top-level
;;(e.g. a package which adds a use-package key word),
;;use `elpaca-wait' to block until that package has been installed/configured.
;;For example:
;;(use-package general :demand t)
;;(elpaca-wait)

(dolist (path '("dotslash-lisp" "dotslash-modules"))
  (add-to-list 'load-path (locate-user-emacs-file path)))
(require 'packages)
(require 'init-highlight)

(load (locate-user-emacs-file "dotslash-local-post.el") :no-error :no-message)

M .config/emacs/init.org => .config/emacs/init.org +5 -0
@@ 2,6 2,11 @@
#+PROPERTY: header-args:elisp  :tangle ~/.config/emacs/init.el
#+auto_tangle: t

#+BEGIN_SRC elisp
;; NOTE:
;; This file is generated from init.org
#+END_SRC

* Variables

#+BEGIN_SRC elisp

M .config/emacs/packages.org => .config/emacs/packages.org +7 -0
@@ 2,6 2,13 @@
#+PROPERTY: header-args:elisp  :tangle ~/.config/emacs/dotslash-modules/packages.el
#+auto_tangle: t


#+BEGIN_SRC elisp
;; NOTE:
;; This file is generated from packages.org
#+END_SRC


* Helpers

Helper variables adopted from Doom Emacs I'm not sure if I'll ever use.