;; 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))
(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)))
;; 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)
(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
'(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))
;; 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*"))))
(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"))
(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
(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)
(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")
;; 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
(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")))
(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"))))))
(use-package evil-collection
:after evil
:config
(setq evil-collection-mode-list '(magit ement term minibuffer help dashboard dired ibuffer tetris))
(evil-collection-init))
(use-package evil-org
:config
(require 'evil-org-agenda)
(evil-org-agenda-set-keys))
(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))
(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 " → " ))
;; 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"))
(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
(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)
(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)))
(use-package diredfl
:hook (dired-mode . diredfl-mode)
:config
(set-face-attribute 'diredfl-dir-heading nil :height 140 :foreground "#cba6f7"))
(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))
(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)
(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))
(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)))))))))
;; 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)))))))
(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)))
;; 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))
(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)))
(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!")))
(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)
(use-package toc-org
:hook ((org-mode markdown-mode) . toc-org-enable))
(use-package org-autolist
:hook (org-mode . org-autolist-mode))
(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
(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
'(("" . "")
("" . "")
("" . "")
("" . "")
("" . ""))))
(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))
(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))
(general-define-key
:states '(normal emacs)
:keymaps 'minibuffer-local-map
"ESC" #'keyboard-escape-quit
"<escape>" #'keyboard-escape-quit)
(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))
(use-package marginalia
:after vertico
:config
(marginalia-mode))
(use-package zoxide
:hook (dired-mode . zoxide-add)
:general
(leader
"f d" '(zoxide-travel :wk "Find directory with Zoxide")))
(use-package affe :defer t)
(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 )"))
(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)))))
(use-package corfu-terminal
:if (not window-system)
:after corfu
:config
(corfu-terminal-mode 1))
(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)
(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))))))
(use-package orderless
:config
(setq completion-styles '(orderless basic)
completion-category-overrides '((file (styles basic partial-completion)))))
(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"))))
(use-package git-gutter
:hook (after-init . (lambda () (add-hook 'find-file-hook #'(lambda ()
(unless (file-remote-p default-directory)
(git-gutter-mode 1)))))))
(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))
(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))))))))))
(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))))
(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")))))
(use-package envrc
:hook (after-init . (lambda () (add-hook 'find-file-hook #'envrc-global-mode)))
:config
(envrc-global-mode 1))
(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
(use-package sideline-flymake
:after sideline
:config
(setq sideline-flymake-display-mode 'line) ; 'line or 'point
(setq sideline-backends-right '(sideline-flymake)))
(use-package yasnippet
:after corfu
:config
(yas-reload-all)
(use-package yasnippet-snippets :ensure t) (yas-reload-all))
(use-package yasnippet-capf
:after yasnippet
:config
(setq yasnippet-capf-lookup-by 'name)
(add-to-list 'completion-at-point-functions #'yasnippet-capf))
(use-package sudo-edit :defer t)
(add-hook 'python-ts-mode-hook #'(lambda()
(setq tab-width 4
indent-tabs-mode nil)))
(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)))
(add-hook 'typescript-ts-mode-hook #'(lambda()
;; (setq-local eglot-ignored-server-capabilities '(:hoverProvider))
(setq tab-width 2
indent-tabs-mode nil)))
(use-package vue-mode
:mode "\\.vue\\'"
:config
(add-hook 'vue-mode-hook #'(lambda()
(setq tab-width 2
indent-tabs-mode nil))))
(add-hook 'go-ts-mode-hook #'(lambda()
(setq tab-width 4
go-ts-mode-indent-offset 4
indent-tabs-mode nil)))
(use-package nix-ts-mode :mode "\\.nix\\'")
(use-package lua-mode
:mode "\\.lua\\'"
:config
(add-hook 'lua-mode-hook #'(lambda()
(setq tab-width 4))))
(use-package elixir-ts-mode :mode "\\.exs\\'")
(add-hook 'rust-ts-mode-hook #'(lambda()
(setq tab-width 4)))
(use-package powershell :mode ("\\.ps1\\'" . powershell-mode))
(use-package markdown-mode :mode "\\.md\\'")
(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))
(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))
(use-package nerd-icons
:config
(setq nerd-icons-font-family "RobotoMono Nerd Font"))
(use-package nerd-icons-dired
:hook (dired-mode . nerd-icons-dired-mode))
(use-package nerd-icons-corfu
:after corfu
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
(use-package nerd-icons-completion
:hook (minibuffer-setup . nerd-icons-completion-mode))
(advice-add 'persp-ibuffer :after #'(lambda (&rest r) (nerd-icons-ibuffer-mode 1)))
(use-package nerd-icons-ibuffer :defer t)
(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"))))
(use-package rainbow-mode
:hook (prog-mode . rainbow-mode))
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
;; 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)))