Skip to content

Latest commit

 

History

History
executable file
·
1668 lines (1607 loc) · 62.9 KB

config.org

File metadata and controls

executable file
·
1668 lines (1607 loc) · 62.9 KB

Justinlime’s Emacs

TABLE OF CONTENTS

General

Late Init

;; revert inflated GC defined in early init to avoid GC pauses while editing
(add-hook 'after-init-hook #'(lambda ()
    (setq file-name-handler-alist file-name-handler-alist-original)
    (makunbound 'file-name-handler-alist-original)))
;; Revert again just in case if out of focus (doesnt work for terminal emacs)
;; and use the chance to collect garbage
(add-hook 'focus-out-hook #'(lambda ()
  (setq gc-cons-threshold (* 1024 1024 16) ; 16mb
        gc-cons-percentage 0.1)
  (garbage-collect)))
;; Revert after 5 seconds of being idle as an additonal precaution
(run-with-idle-timer 5 nil
  (lambda ()
    (setq gc-cons-threshold (* 1024 1024 16) ; 16mb
          gc-cons-percentage 0.1)))

;; Check if running on WSL
(if (string-match-p "Microsoft" (getenv "PATH"))
  (setq jl/is-wsl t)
  (setq jl/is-wsl nil))

Package Manger

(require 'package) ; load the package manager
(setq package-check-signature nil) ; override signature errors
;; add package archives to package manager
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/") t)
(package-initialize) ; exactly what it sounds like 
;; pull archvies and prevent warning messages only on very first startup
(unless package-archive-contents
  (progn
    (setq warning-minimum-level :emergency) 
    (package-refresh-contents)))

Use Package

;; install use-package if it doesn't exist yet
(unless (package-installed-p 'use-package) 
  (package-install 'use-package))          
(eval-when-compile
  (require 'use-package))
;; Make use-package uses `package.el', prevents having to use :ensure t on everything
(setq use-package-always-ensure t) 

Quelpa

(setq quelpa-update-melpa-p nil) ;; stop quelpa from trying to update the melpa all the time
(unless (package-installed-p 'quelpa)
  (with-temp-buffer
    (url-insert-file-contents "https://raw.githubusercontent.com/quelpa/quelpa/master/quelpa.el")
    (eval-buffer)
    (quelpa-self-upgrade)))

Quelpa use-package

(quelpa
 '(quelpa-use-package
   :fetcher git
   :url "https://github.com/quelpa/quelpa-use-package.git"))
(require 'quelpa-use-package)
;; `USAGE'
;; uses the given recipe
;; (use-package abc-mode
;;  :quelpa (abc-mode :fetcher github :repo "mkjunker/abc-mode"))
;; (use-package chan :quelpa (:fetcher))

Preferences

Littering

Buffer Litter

;; Kill *Help* after :q'ing
(advice-add #'evil-quit :after #'(lambda (&rest r)
  (let ((buf (get-buffer "*Help*")))
    (if (eq buf nil) nil
      (kill-buffer buf)))))

;; Disables all startup warnings, and kills message buffer, comment this out when debugging
(setq warning-minimum-level :emergency)
(setq-default message-log-max nil)
(add-hook 'emacs-startup-hook #'(lambda() ;Kills the warning buffer for even emergency messages
  (kill-buffer (get-buffer "*Messages*"))))

File litter

(setq make-backup-files nil ; stop creating ~ files
      auto-save-default nil ; Disable autosave # files
      create-lockfiles nil  ; Disable .# files
      ;; Prevent emacs from trying to write to init.el
      custom-file (concat user-emacs-directory "trash.el"))

GUI

(setq use-dialog-box nil) ; No dialog box
(menu-bar-mode -1) ;Disable menu
(tool-bar-mode -1) ;Disable toolbar
(scroll-bar-mode -1) ;Disable scroll bar
;; Transparency
(let ((opacity 100))
  (set-frame-parameter nil 'alpha-background opacity) ; For current frame
  (add-to-list 'default-frame-alist `(alpha-background . ,opacity))) ; For all new frames henceforth

Scrolling

(setq-default pixel-scroll-precision-mode t
      mouse-wheel-scroll-amount '(1 ((shift) . 1)) ;; one line at a time
      mouse-wheel-progressive-speed nil ;; don't accelerate scrolling
      mouse-wheel-follow-mouse t ;; scroll window under mouse
      mouse-wheel-tilt-scroll t
      scroll-margin 7
      scroll-preserve-screen-position t
      scroll-conservatively 101)

Fonts

(let ((size (if jl/is-wsl 180 120)))
  (set-face-attribute 'default nil
    :font "RobotoMono Nerd Font"
    :height size
    :weight 'medium)
  (set-face-attribute 'variable-pitch nil
    :font "Roboto"
    :height size
    :weight 'medium)
  (set-face-attribute 'fixed-pitch nil
    :font "RobotoMono Nerd Font"
    :height size
    :weight 'medium)
  (set-face-attribute 'italic nil 
    :font "RobotoMono Nerd Font"
    :slant 'italic
    :height size) 
  (set-face-attribute 'bold nil 
    :font "RobotoMono Nerd Font"
    :weight 'ultra-bold
    :height size) 
  (set-face-attribute 'bold-italic nil 
    :font "RobotoMono Nerd Font"
    :weight 'ultra-bold
    :slant 'italic
    :height size))
(setq-default line-spacing 0.10)
(set-language-environment "UTF-8")

Misc

;; Enable mouse in term-mode
(unless (display-graphic-p)
  (xterm-mouse-mode 1)
  ;; let terminal emacs use system clipboard
  ;; check out the xclip package if this doesn't work
  (setq xterm-extra-capabilities '(getSelection setSelection)))
(setq blink-cursor-mode nil ; Exactly what is sounds like
      use-short-answers t ; Set y or n instead of yes or no for questions
      display-line-numbers-type 'relative ; Realive line numbers
      frame-resize-pixelwise t) ; Better frame resizing
(setq-default tab-width 2 ; self explanitory
              indent-tabs-mode nil ; use spaces not tabs
              truncate-lines t) ;Allow truncated lines
(electric-pair-mode 1) ; Auto closing pairs like () and {}
(save-place-mode) ; Save cursor position in buffer on reopen
(global-hl-line-mode) ; Highlight the current line
(electric-indent-mode t) ; Auto Indent
(global-display-line-numbers-mode 1) ; Display line numbers
(global-prettify-symbols-mode) ; prettyyyyyyy

Configurations

Keybinds

General.el

(use-package general
  :config
    (general-define-key 
      :keymaps 'indent-rigidly-map
        "TAB" #'indent-rigidly-right-to-tab-stop
        "<tab>" #'indent-rigidly-right-to-tab-stop
        "DEL" #'indent-rigidly-left-to-tab-stop
        "<backtab>" #'indent-rigidly-left-to-tab-stop
        "h" #'indent-rigidly-left
        "l" #'indent-rigidly-right)
    ;; set up 'SPC' as the global leader key
    (general-create-definer leader
      :states '(normal insert visual emacs motion)
      :keymaps 'override
      :prefix "SPC" ;; set leader
      :global-prefix "M-SPC") ;; access leader in insert mode
    (leader
      "b k" '(kill-this-buffer :wk "Kill this buffer")
      "b r" '(revert-buffer :wk "Reload this buffer"))
    (leader
      "e" '(:ignore t :wk "Evaluate")    
      "e b" '(eval-buffer :wk "Evaluate elisp in buffer")
      "e e" '(eval-expression :wk "Evaluate and elisp expression")
      "e r" '(eval-region :wk "Evaluate selected elisp")) 
    (leader
      "h" '(:ignore t :wk "Help")
      "h f" '(describe-function :wk "Help function")
      "h v" '(describe-variable :wk "Help variable")
      "h m" '(describe-mode :wk "Help mode")
      "h c" '(describe-char :wk "Help character")
      "h k" '(describe-key :wk "Help key/keybind"))
    (leader
      "c r" '(comment-region :wk "Comment selection")
      "c l" '(comment-line :wk "Comment line"))
    (leader
      "f f" '(find-file :wk "Find File"))
    (leader
      "i r" '(indent-rigidly :wk "Indent Rigidly")))

Evil

Evil Mode

(use-package evil
  :general
    (leader
      "w" '(:ignore t :wk "Window Navigation")
      "w h" '(evil-window-left :wk "Move left to window")
      "w <left>" '(evil-window-left :wk "Move left to window")
      "w j" '(evil-window-down :wk "Move down to window")
      "w <down>" '(evil-window-down :wk "Move down to window")
      "w k" '(evil-window-up :wk "Move up to window")
      "w <up>" '(evil-window-up :wk "Move up to window")
      "w l" '(evil-window-right :wk "Move right to window")
      "w <right>" '(evil-window-right :wk "Move right to window")
      "w s" '(evil-window-split :wk "Split window horizontally")
      "w v" '(evil-window-vsplit :wk "Split window vertically"))
    (:states 'insert
      "<tab>" #'tab-to-tab-stop
      "TAB" #'tab-to-tab-stop)
    (:states '(normal insert visual emacs)
      "C-u" #'evil-scroll-up
      "C-d" #'evil-scroll-down)
    (:states '(normal emacs)
      "J" #'shrink-window
      "K" #'enlarge-window
      "H" #'shrink-window-horizontally
      "L" #'enlarge-window-horizontally
      "u" #'undo-tree-undo
      "R" #'undo-tree-redo)
  :init      ;; tweak evil's configuration before loading it
    (setq evil-want-integration t ;; This is optional since it's already set to t by default.
          evil-want-keybinding nil
          evil-vsplit-window-right t
          evil-split-window-below t
          evil-shift-width 4)
    (evil-mode)
  :config
    ;; These hooks may not work if TERM isnt xterm/xterm256
    ;; Let cursor change based on mode when using emacs in the terminal
    (unless (display-graphic-p)
      (add-hook 'post-command-hook #'(lambda ()
        (setq visible-cursor nil) 
        (if (eq evil-state 'insert)
          (send-string-to-terminal "\e[5 q")
          (send-string-to-terminal "\e[2 q"))))))

Evil Collection

(use-package evil-collection
  :after evil
  :config
    (setq evil-collection-mode-list '(magit ement term minibuffer help dashboard dired ibuffer tetris))
    (evil-collection-init))

Evil Org Agenda

(use-package evil-org
  :config
    (require 'evil-org-agenda)
    (evil-org-agenda-set-keys))

Keychord

(use-package key-chord
  :hook (evil-insert-state-entry . key-chord-mode)
  :config
    (setq key-chord-two-keys-delay 1
          key-chord-one-key-delay 1.2
          key-chord-safety-interval-forward 0.1
          key-chord-safety-interval-backward 1)
    (key-chord-define evil-insert-state-map  "nn" 'evil-normal-state)
    (key-chord-define evil-insert-state-map  "jj" 'evil-normal-state))

Which Key

(use-package which-key
  :config
    (which-key-mode 1)
    (setq which-key-side-window-location 'bottom
      which-key-sort-order #'which-key-key-order-alpha
      which-key-sort-uppercase-first nil
      which-key-add-column-padding 1
      which-key-max-display-columns nil
      which-key-min-display-lines 6
      which-key-side-window-slot -10
      which-key-side-window-max-height 0.25
      which-key-idle-delay 0.8
      which-key-max-description-length 25
      which-key-allow-imprecise-window-fit t
      which-key-separator "" ))

Toggle Term

;; This is sphagetti code but I dont care
(let ((init-term "*toggle-term-main*"))
  (defvar jl/active-toggle-term `(("main" ,init-term)) "The most recently used toggle term")
  (defvar jl/current-toggles `(("main" (,init-term))) "List of the current toggle terms"))

(defun jl/toggle-term (&optional name command)
  "Toggle a terminal window with $SHELL, and make it the active term

  If NAME is not provided, prompt to user for one. Closes any other toggle-terms
  currently open that aren't NAME

  NAME can be in the *name* format or just name, but the buffers' name will always output to *name*

  Integrated with perspective.el

  If COMMAND is set, the created terminal will execute the command using your shell's -c flag"
  (interactive)
  ;; Create a key and default value for the alist if it doesnt exist for the perspective 
  (if (eq (assoc (persp-current-name) jl/current-toggles) nil)
    (progn
      (let ((wrapped-name (format "*toggle-term-%s*" (persp-current-name))))
        (add-to-list 'jl/current-toggles `(,(persp-current-name) (,wrapped-name)))
        (setq name wrapped-name)
        (setq jl/active-toggle-term (cons `(,(persp-current-name) ,wrapped-name) jl/active-toggle-term)))))
  (let* (
    (current-toggles-persp (car (cdr (assoc (persp-current-name) jl/current-toggles))))
    ;; Wrapping and unwrapping to force a *name* naming scheme
    (name (if name name (completing-read "Toggle-Term: " current-toggles-persp)))
    (unwrapped-name (replace-regexp-in-string "\\*" "" name))
    (wrapped-name (format "*%s*" unwrapped-name))) 
      ;; Check if another toggle-term is active
      (dolist (c current-toggles-persp)
        (if (string-equal c wrapped-name)
          nil
          (let ((w (get-buffer-window c)))
            (if w (delete-window w)))))
        ;; Toggle the term
        (let (
          (height (window-total-height))
          (window (get-buffer-window wrapped-name)))
            (if window (delete-window window)
                ;; Creates a window below the current window at 22% of the windows height
                (select-window (split-root-window-below (round (* height 0.78))))
                ;; If command is provided, start the term using the shells -c flag
                (if (eq command nil)
                  (make-term unwrapped-name (getenv "SHELL")) ; The make-term function automatically wraps *'s around the name given, hence the unwrapped name being used.
                  (make-term unwrapped-name (getenv "SHELL") nil "-c" command)) 
                ;; Sets the active terminal for the current perspective in the alist
                (let ((key (assoc (persp-current-name) jl/active-toggle-term)))
                  (if key
                    (setcdr key `(,wrapped-name))
                    (setq jl/active-toggle-term (cons `(,(persp-current-name) ,wrapped-name) jl/active-toggle-term))))
                ;; Adds the terminal to the current-toggles alist if its not already included
                (if (member wrapped-name current-toggles-persp) nil
                  (let* ((key (assoc (persp-current-name) jl/current-toggles))
                         (orig-list (car(cdr key))))
                    (if key
                      (progn
                        (add-to-list 'orig-list wrapped-name)
                        (setcdr key `(,orig-list)))
                      (add-to-list 'jl/current-toggles `(,(persp-current-name) (,wrapped-name))))))
                ;; Switch to the buffer and enter insert mode
                (switch-to-buffer wrapped-name)
                (evil-insert 1)))))

(defun jl/toggle-active-term ()
  "Toggle the most recently used toggle-term"
  (interactive)
  (jl/toggle-term (car (cdr (assoc (persp-current-name) jl/active-toggle-term)))))
(leader
  "t t" '(jl/toggle-active-term :wk "Toggle the active toggle-term")
  "t f" '(jl/toggle-term :wk "Find a toggle-term, or create a new one"))

Tramp

(defun jl/ssh (host formatter)
  (persp-switch host)
  (let ((format-host (format formatter host host)))
    (find-file format-host)
    (jl/toggle-term host (format "ssh %s" host))))
(defun jl/ssh-root (host)
  "SSH with sudo privledges using a host from .ssh/config"
  (interactive "sEnter host: ")
  (jl/ssh host "/ssh:%s|sudo:%s:/"))
(defun jl/ssh-user (host)
  "SSH using a host from .ssh/config"
  (interactive "sEnter host: ")
  (jl/ssh host "/ssh:%s:~"))
(defun jl/samba (host)
  (interactive "sEnter user@ip: ")
  (find-file (format "/smb:%s:" host)))
(leader
  "s u" '(jl/ssh-user :wk "SSH as user, using the ssh config file")
  "s r" '(jl/ssh-root :wk "SSH as user with root privledges, using the ssh config file")
  "s m" '(jl/samba :wk "Access an SMB share"))

;; Prevent tramp from trying to save to auth-info
;; It stores passwords in plain text (WTF...)
(connection-local-set-profile-variables
 'remote-without-auth-sources '((auth-sources . nil)))
(connection-local-set-profiles
 '(:application tramp) 'remote-without-auth-sources)

;; Optimization
(with-eval-after-load 'tramp
  (add-to-list 'tramp-connection-properties
                  (list "/ssh:" "direct-async-process" t)
                  (list "/rsync:" "direct-async-process" t))
  (setq tramp-inline-compress-start-size 1000
        tramp-copy-size-limit 10000
        vc-handled-backends '(git)
        tramp-verbose 1 ; shut the fuck up tramp
        password-cache-expiry nil ; stop tramp from forgetting passwords
        ;; force tramp to use the default .ssh config for controlmaster
        ;; makes things quicker and retains passwords
        tramp-use-ssh-controlmaster-options t
        ;; Let tramp re-use the ssh connection
        ;; The preferred way to do this is to add the following
        ;; to your ~/.ssh/config:
        ;; 
        ;; Host *
        ;;   ControlMaster auto
        ;;   ControlPath ~/.ssh/master-%r@%h:%p
        ;;   Compression yes
        ;;   ControlPersist 15m
        tramp-ssh-controlmaster-options (concat
          "-o ControlMaster=auto "
          "-o ControlPath=~/.ssh/master-%%r@%%h:%%p "
          "-o ControlPersist=15m ")
        remote-file-name-inhibit-cache nil)) ; remember more filenames

Dired

(defun jl/dired-open ()
  "Open path in the same buffer if a file, and a new one if a directory in dired"
  (interactive)
  (let ((file (dired-get-file-for-visit)))
    (if (member (file-name-extension file) '("mp4" "mkv" "mov" "flac" "webm" "mp3" "ogg" "opus" "aac"))
      (apply #'start-process "" nil "mpv" (dired-get-marked-files))
      (dired-find-file))))
;; dont prompt ever time for recursion
(setq dired-listing-switches "-alFh --group-directories-first"
      dired-recursive-copies 'always
      large-file-warning-threshold nil
      dired-recursive-deletes 'always)
(add-hook 'dired-mode-hook #'(lambda () 
  (setq-local hl-line-face
                '(:foreground "#11111B"  :background "#89b4fa" :extend t)
              cursor-type nil
              evil-force-cursor t
              auto-revert-verbose nil)
  (dired-omit-mode)
  (auto-revert-mode)
  (dired-hide-details-mode)
  (display-line-numbers-mode -1)))
(put 'dired-find-alternate-file 'disabled nil) ;Allow "dired-find-alternate-file to work without a prompt"
(advice-add #'dired-do-delete :after #'(lambda(&rest r) (dired-unmark-all-marks) (revert-buffer)))
(advice-add #'dired-do-rename :after #'(lambda(&rest r) (dired-unmark-all-marks) (revert-buffer)))
(advice-add #'dired-do-flagged-delete :after #'(lambda(&rest r) (dired-unmark-all-marks) (revert-buffer)))
(general-define-key
  :states 'normal
  :keymaps 'dired-mode-map
    "s" #'dired-hide-details-mode
    "l" #'jl/dired-open
    "d" nil
    "h" #'(lambda () (interactive) (find-file ".."))
    "A" #'dired-create-directory
    "a" nil
    "W" #'wdired-change-to-wdired-mode
    "RET" #'jl/dired-open)

wDired

(setq wdired-allow-to-change-permissions t)
(general-define-key
  :states 'normal
  :keymaps 'wdired-mode-map
    "W" #'wdired-finish-edit
    "<escape>" #'wdired-exit)
; fix icons looking weird after exiting 
(advice-add #'wdired-exit :after #'(lambda (&rest r) (revert-buffer)))

Diredfl

(use-package diredfl 
  :hook (dired-mode . diredfl-mode)
  :config
    (set-face-attribute 'diredfl-dir-heading nil :height 140 :foreground "#cba6f7"))

Dired Filter

(use-package dired-filter
  :general (:keymaps 'dired-mode-map 
            :states 'normal
              "/" #'dired-filter-by-name
              ";" #'dired-filter-pop-all)
  :config
  (setq dired-filter-revert 'always))

Dired Ranger

(quelpa-use-package-activate-advice)
(use-package dired-ranger
  :quelpa (dired-ranger :fetcher github :repo "justinlime/dired-hacks")
  :general 
    (:keymaps 'dired-mode-map 
     :states '(normal emacs motion)
       "y" #'dired-ranger-copy
       "P" #'dired-ranger-paste
       "M" #'dired-ranger-move
       "S" #'dired-ranger-symlink
       "L" #'dired-ranger-symlink-relative
       "H" #'dired-ranger-hardlink)
  :config
    ;; remove marks after an action, and also revert buffer to fix broken icons/formatting after
    ;; moving or pasting
    (advice-add #'dired-ranger-copy :after #'(lambda(&rest r) (dired-unmark-all-marks)))
    (advice-add #'dired-ranger-move :after #'(lambda(&rest r) (dired-unmark-all-marks) (revert-buffer)))
    (advice-add #'dired-ranger-paste :after #'(lambda(&rest r) (dired-unmark-all-marks) (revert-buffer)))
    (setq dired-ranger-copy-ring-size 1)) ;; only keep latest copy in memory
(quelpa-use-package-deactivate-advice)

Dired Subtree

(use-package dired-subtree
  :after diredfl
  :config 
    ;; force subtree to respect omit settings
    (add-hook 'dired-subtree-after-insert-hook #'(lambda ()
      (dired-omit-mode 1)))
    (set-face-attribute 'dired-subtree-depth-1-face nil :background nil)
    (set-face-attribute 'dired-subtree-depth-2-face nil :background nil)
    (set-face-attribute 'dired-subtree-depth-3-face nil :background nil)
    (set-face-attribute 'dired-subtree-depth-4-face nil :background nil)
    (set-face-attribute 'dired-subtree-depth-5-face nil :background nil)
    (set-face-attribute 'dired-subtree-depth-6-face nil :background nil))

Dired Sidebar

(use-package dired-sidebar
  :general
    (leader "d t" '(dired-sidebar-toggle-sidebar :wk "Toggle sidebar directory"))
  :config
  (add-hook 'dired-sidebar-mode-hook #'(lambda ()
    (general-define-key
      :keymaps 'local
      :states 'normal
        "l" #'dired-sidebar-find-file
        "h" #'(lambda () (interactive) (dired-sidebar-find-file "../")))
    (face-remap-set-base 'default :background "#181825")
    (display-line-numbers-mode -1)))

  (push 'toggle-window-split dired-sidebar-toggle-hidden-commands)
  (push 'rotate-windows dired-sidebar-toggle-hidden-commands)

  (setq dired-sidebar-use-one-instance t)
  (setq dired-sidebar-theme 'nerd))

Dired Async

(use-package async 
  :hook (dired-mode . dired-async-mode)
  :config
    ;; Autorefresh the buffer if visible and other conditions
    (run-with-timer 0 2 #'(lambda ()
      (dolist (buf (buffer-list))
          (if (get-buffer-window buf)
            (with-current-buffer buf
              (if (and (derived-mode-p 'dired-mode)
                       (not dired-hide-details-mode)
                       (not (derived-mode-p 'wdired-mode))
                       (not (file-remote-p default-directory))
                       (eq evil-state 'normal))
                  (progn
                    (dired-revert)
                    (hl-line-mode)
                    (hl-line-mode)))))))))

Eglot

;; Override the binary being used on startup
(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '((java-ts-mode java-mode) . ("java-language-server")))
  (add-to-list 'eglot-server-programs
               '((nix-ts-mode nix-mode) . ("nixd"))))

(add-hook 'find-file-hook #'(lambda()
    (unless (file-remote-p (buffer-file-name)) 
      (dolist (lang '(go-ts-mode python-ts-mode js-ts-mode
                      typescript-ts-mode rust-ts-mode elixir-ts-mode
                      nix-ts-mode java-ts-mode c-ts-mode
                      bash-ts-mode))
        (if (eq major-mode lang)
          (progn
            (eglot-ensure)))))))

Communication

ERC

(leader 
  "m i" '((lambda () (interactive) (persp-switch "irc") (erc-tls)) :wk "IRC with erc-tls"))

(setq erc-prompt (lambda () (concat (buffer-name) " > " ))
      erc-fill-column 120
      erc-fill-function 'erc-fill-static
      erc-fill-static-center 20)

(use-package erc-hl-nicks 
  :after erc
  :config
    (add-to-list 'erc-modules 'hl-nicks))

(use-package erc-image
  :after erc
  :config
    (add-to-list 'erc-modules 'image)
    (setq erc-image-inline-rescale 300))

(use-package emojify
  :hook (erc-mode . emojify-mode))

(add-hook 'erc-mode-hook #'(lambda ()
  (toggle-truncate-lines) ; truncate lines in erc mode
  (persp-add-buffer (current-buffer)) ; fix erc buffers not being listed in buffer menu when using perspective.el
  (corfu-mode -1)
  (display-line-numbers-mode -1)))

Ement

;; connect with @<username>:host.org
(use-package ement
  :hook (ement-room-mode . (lambda () (display-line-numbers-mode -1)))
  :general
    (leader 
      "m m" '((lambda () (interactive) (persp-switch "matrix") (ement-connect)) :wk "Matrix with ement"))
    (:keymaps 'ement-room-minibuffer-map :states 'insert "RET" #'newline)
  :config
    (setq ement-notify-dbus-p nil))

Term

(add-hook 'term-mode-hook #'(lambda()
  (general-define-key
    :states 'insert
    :keymaps 'term-raw-map
      "TAB" #'(lambda() (interactive) (term-send-raw-string "\t")))
  (face-remap-set-base 'default :background "#11111B")
  (face-remap-set-base 'fringe :background "#11111B")
  (hl-line-mode 'toggle)
  (defface term-background
  '((t (:inherit default :background "#11111B")))
  "Some bullshit to fix term-mode text-background"
  :group 'basic-faces)
    (setf (elt ansi-term-color-vector 0) 'term-background)
    (display-line-numbers-mode -1)))

Org

(setq org-src-preserve-indentation t
      org-hide-emphasis-markers t
      org-pretty-entities t)

(let ((langs 
        (mapcar #'(lambda (lang) `(,lang . t)) 
          '(python lisp awk emacs-lisp eshell clojure calc C shell sed js ocaml scheme sql sqlite perl haskell css lua))))
  (org-babel-do-load-languages 'org-babel-load-languages langs))

(general-define-key
  :states 'normal 
  :keymaps 'org-mode-map
  "RET" #'org-open-at-point
  "<tab>" #'org-cycle
  "TAB" #'org-cycle
  "P" #'jl/org-grim-slurp)

(add-hook 'org-mode-hook #'(lambda ()
  (org-indent-mode)
  (display-line-numbers-mode -1)
  (setq-local electric-indent-mode nil)))

;; Inline images
(defun jl/org-resize-inline ()
  (when (derived-mode-p 'org-mode)
    (save-excursion
      (save-restriction
        (goto-char (point-min))
        ;; Check if the org buffer even has images first
        (when (re-search-forward "\\[\\[.*\\(png\\|jpe?g\\|gif\\|webp\\)\\]\\]" nil :noerror)
          (setq org-image-actual-width (round (* (window-pixel-width) 0.4)))
          (setq-local scroll-conservatively 0)
          (org-display-inline-images t t))))))

(add-hook 'org-mode-hook #'jl/org-resize-inline)
(add-hook 'after-save-hook #'jl/org-resize-inline)
;; Modified from org-rog to work with grim and slurp
(defun jl/org-grim-slurp ()
  "Screenshots an image to an org-file."
  (interactive)
  (if buffer-file-name
      (progn
        (message "Waiting for region selection with mouse...")
        (let* ((filename
               (concat (file-name-nondirectory buffer-file-name)
                       "_"
                       (format-time-string "%Y%m%d_%H%M%S")
                       ".png"))
               (directory-path (file-name-as-directory (expand-file-name (read-file-name "Select screenshot destination directory: " nil default-directory))))
               (full-path (concat directory-path filename))
               (rel-path (file-relative-name full-path default-directory))
               (rel-path-with-dot (if (string-prefix-p "." rel-path) rel-path (concat "./" rel-path)))) ;ensure ./ prefix
          (unless (file-directory-p directory-path)
            (make-directory directory-path t))
          (shell-command (replace-regexp-in-string "\n" "" (format "grim -g \"%s\" %s" (shell-command-to-string "slurp -d -c \"#cba6f7\"") full-path)))
          (insert "[[" rel-path-with-dot "]]")
          (org-display-inline-images t t))
        (message "File created and linked..."))
    (message "You're in a not saved buffer! Save it first!")))

Org Agenda

(setq jl/org-agenda-dir "~/sync/notes/agenda")
(setq org-agenda-files `(,jl/org-agenda-dir)
      org-todo-keywords '((sequence
        "TODO(t)"           ; Generalized
        "IDEA(i)"           ; 
        "WAIT(w)"           ; Something is holding up this task
        "REMIND(r)"           ; Something is holding up this task
        "|"                 ; The pipe necessary to separate "active" states and "inactive" states
        "DONE(d)"           ; Task has been completed
        "CANCELLED(c)")) ; Task has been cancelled
      org-agenda-window-setup 'only-window
      org-agenda-skip-scheduled-if-done t
      org-agenda-skip-timestamp-if-done t
      org-agenda-skip-deadline-if-done t
      org-agenda-start-day "-3d"
      org-agenda-span 18
      org-agenda-start-on-weekday nil
      ;; Holidays
      calendar-holidays
        '((holiday-fixed 1 1 "New Year's Day")
          (holiday-fixed 2 14 "Valentine's Day")
          (holiday-fixed 4 1 "April Fools' Day")
          (holiday-easter-etc -2 "Good Friday")
          (holiday-easter-etc 0 "Easter Sunday")
          (holiday-easter-etc 1 "Easter Monday")
          (holiday-float 5 0 2 "Mother's Day")
          (holiday-float 5 1 -1 "Memorial Day")
          (holiday-float 6 0 3 "Father's Day")
          (holiday-fixed 7 4 "Independence Day")
          (holiday-float 9 1 1 "Labor Day")
          (holiday-fixed 10 31 "Halloween")
          (holiday-float 11 4 4 "Thanksgiving")
          (holiday-fixed 12 24 "Christmas Eve")
          (holiday-fixed 12 25 "Christmas Day")
          (holiday-fixed 12 31 "New Year's Eve"))
      org-agenda-category-icon-alist
        '(("Birthday" ("" nil nil :ascent center))
          ("Holiday" ("" nil nil :ascent center))
          ("Agenda" ("" nil nil :ascent center))
          ("Reminder" ("" nil nil :ascent center)))) 


(add-hook 'org-agenda-mode-hook #'(lambda ()
  (display-line-numbers-mode -1)))

;; Archive all "Agenda" entries that are done automatically
(add-hook 'org-after-todo-state-change-hook #'(lambda ()
  (when (and (string= org-state "DONE")
             (string= (org-get-category) "Agenda"))
    (org-archive-subtree))))

(set-face-attribute 'org-agenda-date-today nil :foreground "#89b4fa")

(leader
  "a" '(:ignore t :wk "Org Agenda")
  "a a" '(org-agenda :wk "Display the org agenda view")
  "a t" '(org-time-stamp :wk "Insert a timestamp")
  "a p" '(org-priority :wk "Set the priority for a tag")
  "a e" '((lambda () (interactive) (find-file (concat jl/org-agenda-dir "/agenda.org"))) :wk "Edit the org agenda file"))
(general-define-key 
  :keymaps 'calendar-mode-map
  :states '(normal insert motion emacs)
  "RET" #'org-calendar-select)

Org Toc

(use-package toc-org
  :hook ((org-mode markdown-mode) . toc-org-enable))

Org Autolist

(use-package org-autolist
  :hook (org-mode . org-autolist-mode))

Org Appear

(use-package org-appear
  :hook (org-mode . org-appear-mode)
  :config
  (setq org-hide-emphasis-markers t		;; A default setting that needs to be t for org-appear
        org-appear-autoemphasis t		;; Enable org-appear on emphasis (bold, italics, etc)
        org-appear-autolinks nil		;; Don't enable on links
        org-appear-autosubmarkers t))	;; Enable on subscript and superscript

Org Modern

(use-package org-modern
  :hook (after-init . (lambda () (add-hook 'find-file-hook #'global-org-modern-mode)))
  :config
    (global-org-modern-mode)
    (setq org-modern-todo nil 
          org-modern-priority nil
          org-modern-tag nil
          org-modern-list
          '((43 . "")
            (45 . "")
            (42 . ""))
          org-modern-fold-stars
          '(("󰴈" . "󰴈")
            ("󰊹" . "󰊹")
            ("󰨑" . "󰨑")
            ("󰗮" . "󰗮")
            ("" . ""))))

Org Roam

(use-package org-roam
  :general
    (leader
      "r" '(:ignore t :wk "Org Roam")
      "r f" '(org-roam-node-find :wk "Find org roam file")
      "r t" '(org-roam-buffer-toggle :wk "Toggle the roam buffer")
      "r c" '(org-capture-finalize :wk "Capture the roam buffer")
      "r i" '(org-roam-node-insert :wk "Insert node link"))
  :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-directory (file-truename "~/sync/notes/roam"))
    (org-roam-db-autosync-mode 1)
    (require 'org-roam-protocol))

Olivetti

(use-package olivetti
  :hook ((org-mode org-agenda-mode) . olivetti-mode)
  :config
    (setq-default olivetti-body-width 0.80)
    (remove-hook 'olivetti-mode-on-hook 'visual-line-mode))

Minibuffer

(general-define-key
  :states '(normal emacs)
  :keymaps 'minibuffer-local-map
  "ESC" #'keyboard-escape-quit
  "<escape>" #'keyboard-escape-quit)

Vertico

(use-package vertico
  :init
    (vertico-mode)
  :general
    (:keymaps 'vertico-map
     :states '(normal insert)
      "RET" #'vertico-directory-enter
      "<tab>" #'vertico-next
      "TAB" #'vertico-next
      "<backspace>" #'vertico-directory-delete-char
      "DEL" #'vertico-directory-delete-char
      "<backtab>" #'vertico-previous))

Marginalia

(use-package marginalia
  :after vertico
  :config
    (marginalia-mode))

Finding

Zoxide

(use-package zoxide
  :hook (dired-mode . zoxide-add)
  :general
    (leader
      "f d" '(zoxide-travel :wk "Find directory with Zoxide")))

Affe

(use-package affe :defer t)

Consult

(defun jl/consult-find-in-dir ()
  "Find a file in a specific directory
   
  Uses Affe if working with local files, and Consult for remote files"
  (interactive)
  (let ((dir (file-name-directory (read-file-name "Find in directory: "))))
    (if (string-prefix-p "/ssh:" default-directory)
      (consult-find dir)
      (affe-find dir))))
(defun jl/consult-find-in-current ()
  "Find a file in the project's directory

  Sets the root of the search to the folders' .git parent path if present

  Uses Affe if working with local files, and Consult for remote files"
  (interactive)
  (let ((dir (vc-root-dir)))
    (if (string-prefix-p "/ssh:" default-directory)
      (if dir
        (consult-find dir)
        (consult-find))
      (if dir
        (affe-find dir)
        (affe-find)))))

(defun jl/consult-grep-in-dir ()
  "Find a word in a specified project/folder

  Uses Ripgrep if working with local files, and Grep for remote files"
  (interactive)
  (let ((dir (file-name-directory (read-file-name "Find in directory: "))))
    (if (string-prefix-p "/ssh:" default-directory)
      (consult-grep dir)
      (consult-ripgrep dir))))

(defun jl/consult-grep-in-current ()
  "Find a word in the current project/folder

  Sets the root of the search to the folders' .git parent path if present

  Uses Ripgrep if working with local files, and Grep for remote files"
  (interactive)
  (let ((dir (vc-root-dir)))
    (if dir
      (if (string-prefix-p "/ssh:" default-directory)
        (consult-grep dir)
        (consult-ripgrep dir))
      (if (string-prefix-p "/ssh:" default-directory)
        (consult-grep)
        (consult-ripgrep)))))
;; TODO: this is a retarded way to do this, find 
;; a way to make your own buffer source instead
(defun jl/switch-to-buffer ()
  "Use consult with perspective buffers, and also filter out dired buffers"
  (interactive)
  (defvar jl/filter-orig-func (plist-get persp-consult-source :items))
  (plist-put persp-consult-source :items #'(lambda () 
    (let ((candidates (funcall jl/filter-orig-func)))
      (delq nil ; for some reason the returned list includes nil instead of just excluding the dired buffers
        (mapcar #'(lambda (buf)
                    (with-current-buffer buf
                      (unless (or (derived-mode-p 'dired-mode)
                                  (derived-mode-p 'magit-mode)) buf))) candidates)))))
   (setq consult-buffer-sources '(persp-consult-source))
   (consult-buffer))

(defun jl/switch-to-dired ()
  "Use consult with perspective buffers, and only show"
  (interactive)
  (defvar jl/filter-orig-func (plist-get persp-consult-source :items))
  (plist-put persp-consult-source :items #'(lambda () 
    (let ((candidates (funcall jl/filter-orig-func)))
      (delq nil ; for some reason the returned list includes nil instead of just excluding the dired buffers
        (mapcar #'(lambda (buf)
                    (with-current-buffer buf
                      (when (derived-mode-p 'dired-mode) buf))) candidates)))))
   (setq consult-buffer-sources '(persp-consult-source))
   (consult-buffer))

(use-package consult
  :demand t ; persp-consult-source needs to be loaded
  :general
    (leader
      "b f" '(jl/switch-to-buffer :wk "Find a buffer, or create a new one")
      "b d" '(jl/switch-to-dired :wk "Find a dired buffer, or create a new one")
      "f b" '(consult-bookmark :wk "Find a bookmark")
      "f r" '(jl/consult-find-in-current :wk "Find file in current dir/project")
      "f R" '(jl/consult-find-in-dir :wk "Find file in specified dir/project")
      "f w" '(jl/consult-grep-in-current :wk "Find word in current dir/project")
      "f W" '(jl/consult-grep-in-dir :wk "Find word in specified dir/project"))
  :config
    ;; Exclude dired buffers from the buffer list and use consult with perspective
    (setq consult-find-args "find . -not ( -path '*/.git*' -prune ) -not ( -path '*.git*' -prune )"))

Completion

Corfu

(use-package corfu
  :hook (after-init . (lambda () (add-hook 'find-file-hook #'global-corfu-mode)))
        (eval-expression-minibuffer-setup . corfu-mode)
        (ement-room-read-string-setup . (lambda () 
          (setq-local completion-at-point-functions 
            '(ement-room--complete-members-at-point ement-room--complete-rooms-at-point cape-emoji))
          (corfu-mode 1)))
  :general
    (:keymaps 'corfu-map :states 'insert
      "SPC" #'corfu-insert-separator
      "<tab>" #'corfu-next
      "TAB" #'corfu-next
      "<backtab>" #'corfu-previous)
  :config
    (corfu-popupinfo-mode)
    (corfu-history-mode)
    (setq corfu-auto t
          corfu-cycle t
          corfu-preselect 'prompt
          corfu-auto-delay 0.05
          corfu-auto-prefix 2
          corfu-popupinfo-delay 0)
    (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)

    ;; Prevent evil from overriding corfu bindings
    (advice-add #'corfu--setup :after #'(lambda(&rest r) (evil-normalize-keymaps)))
    (advice-add #'corfu--teardown :after #'(lambda(&rest r) (evil-normalize-keymaps)))
    (evil-make-overriding-map corfu-map)

    ;; Rice it up 
    (set-face-attribute 'corfu-default nil :background "#1e1e2e")
    (set-face-attribute 'corfu-current nil :background "#2a2e38" :box "#cba6f7")
    (set-face-attribute 'corfu-border nil :background "#89b4fa")
    (set-face-attribute 'corfu-bar nil :background "#cba6f7")

    ;; Quit completion after entering normal mode
    (add-hook 'evil-insert-state-exit-hook #'corfu-quit)

    ;; `SPC' is used as my separator, this comes with some quirks which this advice solves
    ;; 1. If there is a candidate selected, insert it when hitting `SPC'
    ;; 2. If not, insert the seperator like normal
    ;; 3. If there are no candidates, quit completion
    (advice-add 'corfu-insert-separator :after #'(lambda () 
      (if (= corfu--index -1)
          (when (= corfu--total 0) 
            (corfu-quit))
          (corfu-insert)))))

Corfu Terminal

(use-package corfu-terminal
  :if (not window-system)
  :after corfu
  :config
    (corfu-terminal-mode 1))

Cape

(use-package cape :defer t)
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-emoji)
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-elisp-block)
(add-to-list 'completion-at-point-functions #'cape-keyword)

Company Nixos Options

(use-package company-nixos-options 
  :after nix-ts-mode
  :config 
    ;; prevent eglot from overriding
    (add-hook 'eglot-managed-mode-hook #'(lambda()
     (if (derived-mode-p 'nix-ts-mode)
       (setq-local completion-at-point-functions 
         `(,(cape-company-to-capf #'company-nixos-options) cape-dabbrev cape-file cape-keyword))))))

Orderless

(use-package orderless
  :config
    (setq completion-styles '(orderless basic)
          completion-category-overrides '((file (styles basic partial-completion)))))

Git

Magit

(use-package magit 
  :general
    (leader
      "g s" '(magit-stage-file :wk "Stage Files")
      "g S" '(magit-stage-modified :wk "Stage All Files")
      "g u" '(magit-unstage-file :wk "Unstage Files")
      "g U" '(magit-unstage-all :wk "Unstage All Files")
      "g f" '(magit-fetch :wk "Fetch")
      "g F" '(magit-fetch-all :wk "Fetch")
      "g i" '(magit-init :wk "Init")
      "g l" '(magit-log :wk "Log")
      "g b" '(magit-branch :wk "Branch")
      "g d" '(magit-diff :wk "Diff")
      "g c" '(magit-commit :wk "Commit")
      "g r" '(magit-rebase :wk "Rebase")
      "g R" '(magit-reset :wk "Reset")
      "g p" '(magit-push :wk "Push")
      "g P" '(magit-pull :wk "Pull")
      "g m" '(magit :wk "Magit Menu"))
  :config
    (add-hook 'magit-post-stage-hook #'(lambda ()
      (message "Staged"))))

Git Gutter

(use-package git-gutter
  :hook (after-init . (lambda () (add-hook 'find-file-hook #'(lambda ()
    (unless (file-remote-p default-directory)
      (git-gutter-mode 1)))))))

Misc

Dashboard

(defun jl/random-quote ()
  "Generate a random quote for dashboard"
  (interactive)
  (let ((ops '(
    "Hello World!"
    "Whopper Whopper Whopper Whopper Junior Double Triple Whopper"
    "sudo systemctl stop justinlime"
    "sudo systemctl start justinlime"
    "sudo systemctl restart justinlime"
    "White Monster"
    "https://stinkboys.com"
    "Stink Boys Inc. ©"
    "/home/justinlime/.config"
    "No emacs???"))) (nth (random (length ops)) ops)))

(defun jl/random-ascii ()
  "Generate a random quote for dashboard"
  (interactive)
  (let ((ops '(
"
    ⠀⠀⠀⠀⠀⡰⢂⣾⠿⠛⠒⠚⠛⠃⠺⢶⡀⠀⠀⠀⠀⠀⠀⠀⠀
    ⠀⠀⠀⠀⢠⡣⠋⠁⠀⠀⠀⠀⠀⢀⡐⠒⢙⣄⠀⠀⠀⠀⠀⠀⠀
    ⠀⠀⠀⠀⡘⠀⠀⠀⠀⠀⠀⢄⠉⠀⠐⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀
    ⠀⠀⠀⣾⠁⠀⠀⠄⠂⢈⣠⠎⠀⠀⣸⣿⡿⠓⢬⡇⠀⠀⠀⠀⠀
    ⠀⠀⢸⡟⠀⠔⣁⣤⣶⣿⠋⢰⠀⠀⣿⡟⠻⣦⠀⢳⠀⠀⠀⠀⠀
    ⠀⠀⣷⡇⢠⣾⢟⢭⣭⡭⡄⠀⡆⠀⣿⣷⣶⠺⡆⢸⡄⠀⠀⠀⠀
    ⠀⠀⠇⡇⠛⠡⣑⣈⣛⠝⢁⡀⠇⠀⣿⡿⠛⠒⣡⠇⣧⣀⠀⠀⠀
    ⠀⠀⢠⠁⠈⠐⠤⠄⠀⣠⢸⠈⠢⠀⣿⡇⠀⠀⠠⠚⣿⣿⠀⠀⠀
    ⡄⠀⢾⠀⡆⠠⣴⠞⠯⡀⠈⠙⠲⣶⣿⡇⠑⣦⡄⠀⣿⣿⠀⠀⠀
    ⠈⠺⡮⠀⢡⠀⠀⠀⠀⠀⠁⠐⠒⠒⠛⠃⠈⠛⠇⠀⡏⡏⠀⠀⠀
    ⠀⢰⠁⠀⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡄⠀⢷⠀⠀⠀
    ⠀⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠃⠀⢸⠀⠀⠀
    ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡄⠀⠀
    ⠀⠀⢣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠸⡳⡀⠀
    ⠀⠀⠀⠑⢄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣯⣼⡇⠑⣄
"
"
⠀⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝
⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇
⠀⠀⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀
⠀⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀⠀
⠀⠀⠀⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀
⠀⠀⠀⠀⠀⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀
⠀⠀⠀⠀⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
"
"
⠀⠀⠀⠀⠀⠀⢀⣤⣤⡴⢾⠾⠛⠾⠛⠶⠟⠷⡿⠶⡶⢶⣄⣀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⣶⠟⠁⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⣦⡀⠀⠀⠀⠀
⠀⠀⢀⣴⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣆⠀⠀⠀
⠀⠀⣼⠋⠀⠀⢀⣀⣠⡄⠤⣀⣤⣠⣀⣠⣠⢆⢤⡠⣄⣠⠀⠀⠀⠀⢻⡆⠀⠀
⠀⢰⡏⠀⠀⡴⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠢⡀⠀⠀⢿⠀⠀
⠀⢸⡇⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠘⡄⠀⢸⡀⠀
⠀⢸⡇⢸⠀⠀⠀⠀⠀⠀⠀⠐⠉⠀⠀⠀⠈⠉⠀⠀⠀⠀⠀⠀⠀⠘⡄⢸⡇⠀
⠀⢸⡇⢸⠀⠀⠀⠀⠀⠀⢀⠤⠐⠊⠉⠉⠒⠂⠤⠀⠀⠀⠀⠀⠀⠀⡇⢸⡇⠀
⠀⢸⡇⢸⢠⣤⣤⣤⣤⣤⣤⣤⣤⡀⠀⠀⢠⡴⠶⠶⠶⠶⠶⠶⢶⡄⡇⢸⡇⠀
⠀⣸⠇⣸⣾⠃⠀⢀⣀⣒⣰⠀⠸⣷⠾⠷⣾⡇⠀⣮⠕⢒⣄⠀⠘⡟⣷⣸⣧⡀
⣼⢛⣾⡻⢸⠀⢠⠁⢴⡆⠈⡆⠀⡇⠀⠀⢸⡆⠸⡁⠰⡆⠀⣧⢰⡇⠷⢻⠊⣿
⢿⠀⢳⠀⠸⣇⠀⠉⠒⠒⠉⠀⣸⠇⠀⠀⠸⣇⠀⠈⠑⠒⠉⠁⣸⠇⠀⡇⠀⡿
⠸⣇⠘⢰⠀⠈⠛⠓⠒⠒⠒⠛⠉⠀⠀⠀⠀⡉⠛⠛⠛⠛⠛⠋⠁⠀⢣⠁⢸⠇
⠀⢻⣆⡇⠀⠀⠀⠀⠀⠀⠀⣠⠊⠀⠀⠀⠀⠘⢆⠀⠀⠀⠀⠀⠀⠀⢸⣤⡟⠀
⠀⠀⠸⣷⠀⠀⠀⠀⠀⢀⠐⢇⠚⠳⢄⣠⠔⠉⠡⠇⢄⠀⠀⠀⠀⡀⢸⡇⠀⠀
⠀⠀⠀⣿⠀⠈⠒⠒⠈⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠐⠂⠉⠀⣾⠃⠀⠀
⠀⠀⠀⢻⡄⠀⠀⠀⠀⠀⡀⠀⢀⣀⣀⣀⣀⣀⣀⡀⡀⠀⠀⠀⠀⠀⣿⠀⠀⠀
⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠈⠉⠠⣀⣀⣀⣀⡠⠄⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀
⠀⠀⠀⢸⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠀⠀⠀
⠀⠀⠀⠀⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⡟⠀⠀⠀
⠀⠀⠀⠀⠘⣗⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠐⣿⠁⠀⠀⠀
⠀⠀⠀⠀⠀⢿⠀⠀⠀⠀⠈⠑⠒⠤⠤⠤⠤⠤⠐⠊⠁⠀⠀⠀⢠⡏⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢰⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣸⡷⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠙⠛⠶⠶⢤⣤⣤⣤⣤⣤⣤⣤⣤⠤⠶⠶⠛⠋⠁⠀⠀⠀⠀⠀
"
    ))) (nth (random (length ops)) ops)))
(defun jl/random-icon ()
  "Generate a random image for dashboard"
  (interactive)
  (let* ((icons-dir (expand-file-name "icons/" user-emacs-directory))
        (ops (directory-files icons-dir))
        (ops (delete "." ops))
        (ops (delete ".." ops))
        (file (nth (random (length ops)) ops)))
          (expand-file-name file icons-dir)))

(use-package dashboard
  :config
    (setq default-directory "~/"
          dashboard-icon-type 'nerd-icons
          dashboard-set-file-icons t
          dashboard-vertically-center-content t
          dashboard-center-content t
          dashboard-display-icons-p t
          initial-buffer-choice (lambda() (get-buffer-create "*dashboard*")) 
          dashboard-banner-logo-title (jl/random-quote)
          dashboard-footer-messages `(,(jl/random-quote)))
    (if (display-graphic-p)
      (setq dashboard-startup-banner (jl/random-icon))
      (progn
        (setq dashboard-startup-banner 'ascii)
        (setq dashboard-banner-ascii (jl/random-ascii))))
    (dashboard-setup-startup-hook))

Perspective

(use-package perspective
  :init
    (setq persp-suppress-no-prefix-key-warning t)
    (setq persp-initial-frame-name "emacs")
    (persp-mode)
  :general
    (leader
      "b i" '(persp-ibuffer :wk "Buffer Menu (IBuffer)")
      "p f" '(persp-switch :wk "Find perspective, or create new one")
      "p h" '(persp-prev :wk "Previous perspective")
      "p l" '(persp-next :wk "Next perspective")
      "p k" '((lambda () (interactive) (if (yes-or-no-p "Kill the current perspective?")(persp-kill (persp-current-name)))) :wk "Kill the current perspective")
      "p 1" '((lambda () (interactive) (persp-switch-by-number 1)) :wk "Switch to perspective 1")
      "p 2" '((lambda () (interactive) (persp-switch-by-number 2)) :wk "Switch to perspective 2")
      "p 3" '((lambda () (interactive) (persp-switch-by-number 3)) :wk "Switch to perspective 3")
      "p 4" '((lambda () (interactive) (persp-switch-by-number 4)) :wk "Switch to perspective 4")
      "p 5" '((lambda () (interactive) (persp-switch-by-number 5)) :wk "Switch to perspective 5")
      "p 6" '((lambda () (interactive) (persp-switch-by-number 6)) :wk "Switch to perspective 6")
      "p 7" '((lambda () (interactive) (persp-switch-by-number 7)) :wk "Switch to perspective 7")
      "p 8" '((lambda () (interactive) (persp-switch-by-number 8)) :wk "Switch to perspective 8")
      "p 9" '((lambda () (interactive) (persp-switch-by-number 9)) :wk "Switch to perspective 9")
      "p 0" '((lambda () (interactive) (persp-switch-by-number 0)) :wk "Switch to perspective 0"))
  :config
    (require 'ibuffer)
    (setq persp-sort 'created)
    ;; Overriding the function to reverse the sorting order
    (defun persp-names ()
      "Return a list of the names of all perspectives on the `selected-frame'.

    If `persp-sort' is 'name (the default), then return them sorted
    alphabetically. If `persp-sort' is 'access, then return them
    sorted by the last time the perspective was switched to, the
    current perspective being the first. If `persp-sort' is 'created,
    then return them in the order they were created, with the newest
    first."
      (let ((persps (hash-table-values (perspectives-hash))))
        (cond ((eq persp-sort 'created)
                 (mapcar 'persp-name
                   (sort persps (lambda (a b)
                     (time-less-p (persp-created-time a)
                       (persp-created-time b))))))))))

Highlight Indent

(use-package highlight-indent-guides 
  :hook (prog-mode . highlight-indent-guides-mode)
  :config 
    (add-hook 'highlight-indent-guides-mode-hook #'(lambda ()
      (set-face-foreground 'highlight-indent-guides-top-character-face "#cba6f7")
      (setq highlight-indent-guides-responsive 'top
            highlight-indent-guides-method 'character))))

Undo Tree

(use-package undo-tree
  :hook (after-init . (lambda () (add-hook 'find-file-hook #'global-undo-tree-mode)))
  :config
    (setq undo-tree-auto-save-history t)
    (setq undo-tree-history-directory-alist `(("." . ,(concat user-emacs-directory "undo")))))

Direnv

(use-package envrc
  :hook (after-init . (lambda () (add-hook 'find-file-hook #'envrc-global-mode)))
  :config
    (envrc-global-mode 1))

Sideline

(use-package sideline
  :hook (prog-mode . sideline-mode)
  :config
    (set-face-attribute 'sideline-default nil :foreground "#cba6f7")
    (setq sideline-backends-left-skip-current-line t   ; don't display on current line (left)
          sideline-backends-right-skip-current-line t  ; don't display on current line (right)
          sideline-order-left 'down                    ; or 'up
          sideline-order-right 'up                     ; or 'down
          sideline-format-left "%s   "                 ; format for left aligment
          sideline-format-right "   %s"                ; format for right aligment
          sideline-priority 100                        ; overlays' priority
          sideline-display-backend-name t))            ; display the backend name

Sideline Flymake

(use-package sideline-flymake
  :after sideline
  :config
    (setq sideline-flymake-display-mode 'line) ; 'line or 'point
    (setq sideline-backends-right '(sideline-flymake)))

Yasnippet

(use-package yasnippet
  :after corfu
  :config 
    (yas-reload-all)
    (use-package yasnippet-snippets :ensure t) (yas-reload-all))

Yasnippet Capf

(use-package yasnippet-capf 
  :after yasnippet
  :config
    (setq yasnippet-capf-lookup-by 'name)
    (add-to-list 'completion-at-point-functions #'yasnippet-capf))

Sudo Edit

(use-package sudo-edit :defer t)

Programming Modes

Python Mode

(add-hook 'python-ts-mode-hook #'(lambda()
  (setq tab-width 4
        indent-tabs-mode nil)))

Javascript Mode

(add-hook 'js-ts-mode-hook #'(lambda()
  ;; (setq-local eglot-ignored-server-capabilities '(:hoverProvider))
  (setq tab-width 2
        indent-tabs-mode nil
        js-indent-level 2)))

TypeScript Mode

(add-hook 'typescript-ts-mode-hook #'(lambda()
  ;; (setq-local eglot-ignored-server-capabilities '(:hoverProvider))
  (setq tab-width 2
        indent-tabs-mode nil)))

Vue Mode

(use-package vue-mode 
  :mode "\\.vue\\'" 
  :config 
  (add-hook 'vue-mode-hook #'(lambda()
    (setq tab-width 2
          indent-tabs-mode nil))))

Go Mode

(add-hook 'go-ts-mode-hook #'(lambda()
  (setq tab-width 4
        go-ts-mode-indent-offset 4
        indent-tabs-mode nil)))

Nix Mode

(use-package nix-ts-mode :mode "\\.nix\\'")

Lua Mode

(use-package lua-mode 
  :mode "\\.lua\\'"
  :config
  (add-hook 'lua-mode-hook #'(lambda()
    (setq tab-width 4))))

Elixir Mode

(use-package elixir-ts-mode :mode "\\.exs\\'")

Rust Mode

(add-hook 'rust-ts-mode-hook #'(lambda()
  (setq tab-width 4)))

PowerShell Mode

(use-package powershell :mode ("\\.ps1\\'" . powershell-mode))

Markdown Mode

(use-package markdown-mode :mode "\\.md\\'")

Style

Themes

(use-package doom-themes
  :config
    ;; something keeps overriding the cursor color, so run it in a one-shot timer
    (add-hook 'post-command-hook #'(lambda ()
      (set-cursor-color (if (derived-mode-p 'dired-mode) "#89b4fa" "#cba6f7"))))
    (set-face-attribute'line-number-current-line nil :foreground "#cba6f7")
    (set-face-attribute 'org-block nil :background "#181825") ; src blocks
    (set-face-attribute 'default nil :background "#1e1e2e") ; emacs background
    (set-face-attribute 'line-number-current-line nil :background "#1e1e2e") ; current line number
    (setq doom-themes-enable-bold t    ; if nil, bold is universally disabled
          doom-themes-enable-italic t) ; if nil, italics is universally disabled
    (load-theme 'doom-vibrant t)
    ;; Enable flashing mode-line on errors
    (doom-themes-visual-bell-config)
    ;; Corrects (and improves) org-mode's native fontification.
    (doom-themes-org-config))

Modeline

(use-package doom-modeline
  :config
    (doom-modeline-mode 1)
    (display-battery-mode 1)
    (set-face-attribute 'mode-line nil :background "#11111B")
    (set-face-attribute 'mode-line-inactive nil :background "#11111B")
    (display-time-mode))

Nerd Icons

(use-package nerd-icons
  :config
    (setq nerd-icons-font-family "RobotoMono Nerd Font"))

Nerd Icons Dired

(use-package nerd-icons-dired 
  :hook (dired-mode . nerd-icons-dired-mode))

Nerd Icons Corfu

(use-package nerd-icons-corfu 
  :after corfu 
  :config
    (add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))

Nerd Icons Completion

(use-package nerd-icons-completion
  :hook (minibuffer-setup . nerd-icons-completion-mode))

Nerd Icons Ibuffer

(advice-add 'persp-ibuffer :after #'(lambda (&rest r) (nerd-icons-ibuffer-mode 1)))
(use-package nerd-icons-ibuffer :defer t)

Highlight TODO

(use-package hl-todo
  :hook (prog-mode . hl-todo-mode)
  :config
    (setq hl-todo-keyword-faces
        '(("TODO"   . "#FF0000")
          ("FIXME"  . "#FF0000")
          ("DEBUG"  . "#A020F0")
          ("GOTCHA" . "#FF4500")
          ("STUB"   . "#1E90FF"))))

Rainbow Mode

(use-package rainbow-mode
  :hook (prog-mode . rainbow-mode))

Rainbow Delimiters

(use-package rainbow-delimiters 
  :hook (prog-mode . rainbow-delimiters-mode))

Treesitter

;; levels from 1 - 4, higher numbers being more "colorful"
(setq-default treesit-font-lock-level 4)

;; where to source the langs
(setq treesit-language-source-alist
  '((nix "https://github.com/nix-community/tree-sitter-nix")
    (c "https://github.com/tree-sitter/tree-sitter-c")
    (python "https://github.com/tree-sitter/tree-sitter-python")
    (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
    (typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
    (tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src")
    (json "https://github.com/tree-sitter/tree-sitter-json")
    (toml "https://github.com/tree-sitter/tree-sitter-toml")
    (yaml "https://github.com/ikatyang/tree-sitter-yaml")
    (elixir "https://github.com/elixir-lang/tree-sitter-elixir")
    (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
    (rust "https://github.com/tree-sitter/tree-sitter-rust")
    ;; (html "https://github.com/tree-sitter/tree-sitter-html") ;not used yet cant find a good html-ts-mode and I dont feel like making one
    (css "https://github.com/tree-sitter/tree-sitter-css")
    (go "https://github.com/tree-sitter/tree-sitter-go")
    (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
    (java "https://github.com/tree-sitter/tree-sitter-java")
    (bash "https://github.com/tree-sitter/tree-sitter-bash")))

;; auto install any missing defined langs
(dolist (lang treesit-language-source-alist)
  (unless (treesit-language-available-p (car lang))
    (treesit-install-language-grammar (car lang))))

;; maps the ts modes to normal modes
(add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode))
(add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode))
(add-to-list 'major-mode-remap-alist '(sh-mode . bash-ts-mode))
(add-to-list 'major-mode-remap-alist '(css-mode . css-ts-mode))
(add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode))
(add-to-list 'major-mode-remap-alist '(javascript-mode . js-ts-mode))
(add-to-list 'major-mode-remap-alist '(java-mode . java-ts-mode))

;; for modes that have an existing ts mode but no existing normal mode
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))
(add-to-list 'auto-mode-alist '("\\.rs\\'" . rust-ts-mode))
(add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode))
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-ts-mode))
(add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-ts-mode))
(add-to-list 'auto-mode-alist '("\\.json\\'" . json-ts-mode))
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))

;; If you need to override the names of the expected libraries, defualt emacs looks for libtree-sitter-${LANG_NAME}
;; (setq treesit-load-name-override-list
;;    '((cc "libtree-sitter-c")
;;      (gomod "libtree-sitter-go")))

;; Org mode src blocks for treesitter
(setq org-src-lang-modes 
  '(("go" . go-ts)
    ("cpp" . c++-ts)
    ("toml" . toml-ts)
    ("yaml" . toml-ts)
    ("json" . json-ts)
    ("bash" . bash-ts)
    ("rust" . rust-ts)
    ("c" . c-ts)
    ("nix" . nix-ts)
    ("python" . python-ts)
    ("js" . js-ts)
    ("ts" . typescript-ts)
    ("java" . java-ts)
    ("css" . css-ts)))