Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gopar/.emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

262 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Configuration

My Emacs Configuration as an org file cause I wanna be like the cool kids 😎

NOTE

This is a mess, unorganized but it works for me. No plan to clean this up. This config will most likely not work for you since its made for me. We are all different. Thanks for coming to my Ted Talk

Recommended Packages/Snippets to Have As Early as Possible

(setq load-prefer-newer t)
(use-package no-littering :ensure t :demand t)

Built-Ins

Going to configure all the built in packages first And any third party lib that are related to it will go under them.

Emacs Defaults

Customizing some things that should be defaults ¯\_(ツ)_/¯

;; Customize default emacs
(use-package emacs
  :ensure nil
  :defer
  :hook ((after-init . pending-delete-mode)
         (after-init . toggle-frame-maximized)
         (after-init . (lambda () (scroll-bar-mode -1)))
         (prog-mode . (lambda () (setq-local show-trailing-whitespace t))))
  :custom
  ;; flash the frame to represent a bell.
  (visible-bell t)
  (enable-recursive-minibuffers t)
  (debugger-stack-frame-as-list t)
  (narrow-to-defun-include-comments t)
  (use-short-answers t)
  (confirm-nonexistent-file-or-buffer nil)
  ;; Dont Treat manual switching of buffers the same as programatic
  (switch-to-buffer-obey-display-actions nil)
  (switch-to-buffer-in-dedicated-window nil)
  (window-sides-slots '(3 3 3 3))
  ;; Sentences end with 1 space not 2
  (sentence-end-double-space nil)
  ;; make cursor the width of the character it is under
  ;; i.e. full width of a TAB
  (x-stretch-cursor t)
  ;; Stop cursor from going into minibuffer prompt text
  (minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt))
  (history-delete-duplicates t)
  ;; Completion stuff for consult
  (completion-ignore-case t)
  (read-buffer-completion-ignore-case t)
  (completion-cycle-threshold nil)
  (tab-always-indent 'complete)
  (use-dialog-box nil) ; Lets be consistent and use minibuffer for everyting
  (scroll-conservatively 100)
  (frame-inhibit-implied-resize t)
  (custom-file "~/.emacs.d/ignoreme.el")

  :config
  (load custom-file)
  (when (eq system-type 'darwin)
    (setq mac-option-key-is-meta nil
          mac-command-key-is-meta t
          mac-command-modifier 'meta
          mac-option-modifier 'none)
    )
  (setq-default c-basic-offset 4
                c-default-style "linux"
                indent-tabs-mode nil
                fill-column 120
                tab-width 4)
  ;; Replaced in favor for `use-short-answers`
  ;; (fset 'yes-or-no-p 'y-or-n-p)
  (prefer-coding-system 'utf-8)
  ;; Uppercase is same as lowercase
  (define-coding-system-alias 'UTF-8 'utf-8)
  ;; Enable some commands
  (put 'upcase-region 'disabled nil)
  (put 'downcase-region 'disabled nil)
  (put 'erase-buffer 'disabled nil)
  (put 'list-timers 'disabled nil)
  ;; C-x n <key> useful stuff
  (put 'narrow-to-region 'disabled nil)
  (tool-bar-mode -1)
  (menu-bar-mode -1)
  (command-query 'save-buffers-kill-terminal "Do you really want to kill emacs?")

  :bind (("C-z" . nil)
         ("C-x C-z" . nil)
         ("C-x C-k RET" . nil)
         ("M-SPC" . nil)
         ("RET" . newline-and-indent)
         ("C-j" . newline)
         ("M-\\" . cycle-spacing)
         ("C-x \\" . align-regexp)
         ("C-x C-b" . ibuffer)
         ("M-u" . upcase-dwim)
         ("M-l" . downcase-dwim)
         ("M-c" . capitalize-dwim)
         ("C-S-k" . gopar/delete-line-backward)
         ("C-k" . gopar/delete-line)
         ("M-d" . gopar/delete-word)
         ("<M-backspace>" . gopar/backward-delete-word)
         ("M-e" . gopar/next-sentence)
         ("M-a" . gopar/last-sentence)
         (";" . gopar/easy-underscore)
         ("C-x k" . (lambda () (interactive) (bury-buffer)))
         ("C-x C-k" . (lambda () (interactive) (kill-buffer))))

  :init
  (setq-default line-spacing 0.1)

  (defun gopar/er-keyboard-quit-dwim ()
    ;; Copied from https://emacsredux.com/blog/2025/06/01/let-s-make-keyboard-quit-smarter/
    "Do-What-I-Mean behaviour for a general `keyboard-quit'.

The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open.  Whereas we want it to close the
minibuffer, even without explicitly focusing it.

The DWIM behaviour of this command is as follows:

- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
    (interactive)
    (cond
     ((region-active-p)
      (keyboard-quit))
     ((derived-mode-p 'completion-list-mode)
      (delete-completion-window))
     ((> (minibuffer-depth) 0)
      (abort-recursive-edit))
     (t
      (keyboard-quit))))
  (global-set-key [remap keyboard-quit] #'gopar/er-keyboard-quit-dwim)


  (defun gopar/copy-filename-to-kill-ring ()
    (interactive)
    (kill-new (buffer-file-name))
    (message "Copied to file name kill ring"))

  (defun gopar/easy-underscore (arg)
    "Convert all inputs of semicolon to an underscore.
If given ARG, then it will insert an acutal semicolon."
    (interactive "P")
    (if arg
        (insert ";")
      (insert "_")))

  (defun easy-camelcase (arg)
    (interactive "c")
    ;; arg is between a-z
    (cond ((and (>= arg 97) (<= arg 122))
           (insert (capitalize (char-to-string arg))))
          ;; If it's a new line
          ((= arg 13)
           (newline-and-indent))
          ((= arg 59)
           (insert ";"))
          ;; We probably meant a key command, so lets execute that
          (t (call-interactively
              (lookup-key (current-global-map) (char-to-string arg))))))

  ;; Stolen from https://emacs.stackexchange.com/a/13096/8964
  (defun gopar/reload-dir-locals-for-current-buffer ()
    "Reload dir locals for the current buffer"
    (interactive)
    (let ((enable-local-variables :all))
      (hack-dir-local-variables-non-file-buffer)))

  (defun gopar/delete-word (arg)
    "Delete characters forward until encountering the end of a word.
With argument, do this that many times.
This command does not push text to `kill-ring'."
    (interactive "p")
    (delete-region
     (point)
     (progn
       (forward-word arg)
       (point))))

  (defun gopar/backward-delete-word (arg)
    "Delete characters backward until encountering the beginning of a word.
With argument, do this that many times.
This command does not push text to `kill-ring'."
    (interactive "p")
    (gopar/delete-word (- arg)))

  (defun gopar/delete-line ()
    "Delete text from current position to end of line char.
This command does not push text to `kill-ring'."
    (interactive)
    (delete-region
     (point)
     (progn (end-of-line 1) (point)))
    (delete-char 1))

  (defadvice gopar/delete-line (before kill-line-autoreindent activate)
    "Kill excess whitespace when joining lines.
If the next line is joined to the current line, kill the extra indent whitespace in front of the next line."
    (when (and (eolp) (not (bolp)))
      (save-excursion
        (forward-char 1)
        (let ((start (point)))
          (skip-chars-forward " \t")
          (delete-region start (point))
          (insert " ")))))

  (defun gopar/delete-line-backward ()
    "Delete text between the beginning of the line to the cursor position.
This command does not push text to `kill-ring'."
    (interactive)
    (let (p1 p2)
      (setq p1 (point))
      (beginning-of-line 1)
      (setq p2 (point))
      (delete-region p1 p2)))

  (defun gopar/next-sentence ()
    "Move point forward to the next sentence.
Start by moving to the next period, question mark or exclamation.
If this punctuation is followed by one or more whitespace
characters followed by a capital letter, or a '\', stop there. If
not, assume we're at an abbreviation of some sort and move to the
next potential sentence end"
    (interactive)
    (re-search-forward "[.?!]")
    (if (looking-at "[    \n]+[A-Z]\\|\\\\")
        nil
      (gopar/next-sentence)))

  (defun gopar/list-git-authors-for-file ()
    "Display all the authors for a given file.
If file is not in a git repo or file is not a real file (aka buffer), then do nothing."
    (interactive)
    (let* ((file (buffer-file-name))
           (root (when (vc-root-dir) (expand-file-name (vc-root-dir))))
           (file (when (and file root) (s-chop-prefix root file))))
      (when (and root file)
        (message (format "Contributors for %s:\n%s" file (shell-command-to-string
          (format "git shortlog HEAD -s -n %s" file)))))))

  (defun gopar/last-sentence ()
    "Does the same as 'gopar/next-sentence' except it goes in reverse"
    (interactive)
    (re-search-backward "[.?!][   \n]+[A-Z]\\|\\.\\\\" nil t)
    (forward-char))

  (defun gopar/add-env-vars ()
    "Setup environment variables that I will need."
    (load-file "~/.emacs.d/etc/eshell/set_env.el")
    (setq-default eshell-path-env (getenv "PATH"))
    (setq exec-path (append exec-path
                            `("/usr/local/bin"
                              "/usr/bin"
                              "/usr/sbin"
                              "/sbin"
                              "/bin"
                              "/Users/gopar/.nvm/versions/node/v22.14.0/bin"
                              "/opt/homebrew/opt/llvm/bin/"
                              )
                            (split-string (getenv "PATH") ":")))
    (setenv "PATH" (concat "/Users/gopar/.nvm/versions/node/v22.14.0/bin:" (getenv "PATH")))))

Diary

(use-package calendar
  :ensure nil
  :defer
  :mode ("\\diary\\'" . diary-mode)
  :custom
  (diary-file (concat user-emacs-directory "etc/diary"))
  (diary-display-function 'ignore)
  (calendar-mark-diary-entries-flat t)
  (diary-comment-start ";;")
  (diary-comment-end ""))

Org Mode

Org

Main configuration

(use-package org
  :defer
  :custom
  (org-agenda-include-diary t)
  ;; Where the org files live
  (org-directory "~/.emacs.d/org/")
  ;; Where archives should go
  (org-archive-location (concat (expand-file-name "~/.emacs.d/org/private/org-roam/gtd/archives.org") "::"))
  ;; Make sure we see syntax highlighting
  (org-src-fontify-natively t)
  (org-tag-alist '(("emacs" . ?e)
                   ("car" . ?c)
                   ("home" . ?o)
                   ("project" . ?p)
                   ("fun" . ?f)
                   ("health" . ?h)
                   ("misc" . ?i)
                   ("tv" . ?t)
                   ("money" . ?m)))
  ;; I dont use it for subs/super scripts
  (org-use-sub-superscripts nil)
  ;; Should everything be hidden?
  (org-startup-folded t)
  (org-M-RET-may-split-line '((default . nil)))
  ;; Hide stars
  (org-hide-leading-stars t)
  (org-hide-emphasis-markers nil)
  ;; Show as utf-8 chars
  (org-pretty-entities t)
  ;; put timestamp when finished a todo
  (org-log-done 'time)
  ;; timestamp when we reschedule
  (org-log-reschedule t)
  ;; Don't indent the stars
  (org-startup-indented nil)
  (org-list-allow-alphabetical t)
  (org-image-actual-width nil)
  ;; Save notes into log drawer
  (org-log-into-drawer t)
  ;;
  (org-fontify-whole-heading-line t)
  (org-fontify-done-headline t)
  ;;
  (org-fontify-quote-and-verse-blocks t)
  ;; See down arrow instead of "..." when we have subtrees
  ;; (org-ellipsis "⤵")
  ;; catch invisible edit
  ( org-catch-invisible-edits 'show-and-error)
  ;; Only useful for property searching only but can slow down search
  (org-use-property-inheritance t)
  ;; Count all children TODO's not just direct ones
  (org-hierarchical-todo-statistics nil)
  ;; Unchecked boxes will block switching the parent to DONE
  (org-enforce-todo-checkbox-dependencies t)
  ;; Don't allow TODO's to close without their dependencies done
  (org-enforce-todo-dependencies t)
  (org-track-ordered-property-with-tag t)
  ;; Where should notes go to? Dont even use them tho
  (org-default-notes-file (concat org-directory "notes.org"))
  ;; The right side of | indicates the DONE states
  (org-todo-keywords
   '((sequence "TODO(t)" "NEXT(n)" "IN-PROGRESS(i!)" "WAITING(w!)" "|" "DONE(d!)" "CANCELED(c!)" "DELEGATED(p!)")))
  ;; Needed to allow helm to compute all refile options in buffer
  (org-outline-path-complete-in-steps nil)
  (org-deadline-warning-days 2)
  (org-log-redeadline t)
  (org-log-reschedule t)
  ;; Repeat to previous todo state
  ;; If there was no todo state, then dont set a state
  (org-todo-repeat-to-state t)
  ;; Refile options
  (org-refile-use-outline-path 'file)
  (org-refile-allow-creating-parent-nodes 'confirm)
  (org-refile-targets '(("~/.emacs.d/org/private/org-roam/gtd/gtd.org" :maxlevel . 3)
                        ("~/.emacs.d/org/private/org-roam/gtd/someday.org" :level . 1)
                        ("~/.emacs.d/org/private/org-roam/gtd/tickler.org" :maxlevel . 1)
                        ("~/.emacs.d/org/private/org-roam/gtd/repeat.org" :maxlevel . 1)))
  ;; Lets customize which modules we load up
  (org-modules '(;; ol-eww
                 ;; Stuff I've enabled below
                 org-habit
                 ;; org-checklist
                 ))
  (org-special-ctrl-a/e t)
  (org-insert-heading-respect-content t)
  :hook ((org-mode . org-indent-mode)
         (org-mode . org-display-inline-images))
  :custom-face
  (org-scheduled-previously ((t (:foreground "orange"))))
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((sql . t)
     (sqlite . t)
     (python . t)
     (java . t)
     ;; (cpp . t)
     (C . t)
     (emacs-lisp . t)
     (shell . t)))
  ;; Save history throughout sessions
  (org-clock-persistence-insinuate))

Org Tempo

(use-package org-tempo
  :after org
  :config
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("p" . "src python"))
  (add-to-list 'org-structure-template-alist '("j" . "src java"))
  (add-to-list 'org-structure-template-alist '("k" . "src kotlin"))
  (add-to-list 'org-structure-template-alist '("sh" . "src sh"))
  (add-to-list 'org-structure-template-alist '("ss" . "src sql"))
  )

Org Clock

(use-package org-clock
  :after org
  :custom
  ;; Save clock history accross emacs sessions (read var for required info)
  (org-clock-persist t)
  ;; If idle for more than 15 mins, resolve by asking what to do with clock
  (org-clock-idle-time 15)
  ;; Don't show current clocked in task
  (org-clock-clocked-in-display nil)
  ;; Show more clocking history
  (org-clock-history-length 10)
  ;; Include running time in clock reports
  (org-clock-report-include-clocking-task t)
  ;; Put all clocking info int the "CLOCKING" drawer
  (org-clock-into-drawer "CLOCKING")
  ;; Setup default clocktable summary
  (org-clock-clocktable-default-properties
   '(:maxlevel 2 :scope file :formula % ;; :properties ("Effort" "Points")
               :sort (5 . ?t) :compact t :block today))
  :bind (:map global-map
              ("C-c j" . (lambda () (interactive) (org-clock-goto)))
              :map org-mode-map
              ("C-c C-x r" . (lambda () (interactive) (org-clock-report)))))

Org Agenda

(use-package org-agenda
  :defer
  :commands org-agenda
  :bind (("C-c a" . org-agenda))
  :hook ((org-agenda-finalize . hl-line-mode)
         (org-agenda-finalize . org-agenda-entry-text-mode))
  :custom
  (org-agenda-current-time-string (if (and (display-graphic-p)
           (char-displayable-p ?←)
           (char-displayable-p ?─))
      "⬅️ now"
    "now - - - - - - - - - - - - - - - - - - - - - - - - -"))
  (org-agenda-timegrid-use-ampm t)
  (org-agenda-tags-column 0)
  (org-agenda-window-setup 'only-window)
  (org-agenda-restore-windows-after-quit t)
  (org-agenda-log-mode-items '(closed clock state))
  (org-agenda-time-grid '((daily today require-timed)
                          (600 800 1000 1200 1400 1600 1800 2000)
                          " ┄┄┄┄┄ " "┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"))
  ;; (org-agenda-start-with-log-mode '(closed clock state))
  (org-agenda-files "~/.emacs.d/org/agenda-files.org")
  ;; (org-agenda-todo-ignore-scheduled 'future)
  ;; TODO entries that can't be marked as done b/c of children are shown as dimmed in agenda view
  (org-agenda-dim-blocked-tasks 'invisible)
  ;; Start the week view on whatever day im on
  (org-agenda-start-on-weekday nil)
  ;; How to identify stuck/non-stuck projects
  ;; Projects are identified by the 'project' tag and its always the first level
  ;; Next any of these todo keywords means it's not a stuck project
  ;; 3rd, theres no tags that I use to identify a stuck Project
  ;; Finally, theres no special text that signify a non-stuck project
  (org-stuck-projects
   '("+project+LEVEL=1"
     ("IN-PROGRESS" "WAITING" "DONE" "CANCELED" "DELEGATED")
     nil
     ""))
  (org-agenda-prefix-format
   '((agenda . " %-4e %i %-12:c%?-12t% s ")
     (todo . " %i %-10:c %-5e %(gopar/get-schedule-or-deadline-if-available)")
     (tags . " %i %-12:c")
     (search . " %i %-12:c")))
  ;; Lets define some custom cmds in agenda menu
  (org-agenda-custom-commands
   '(("h" "Agenda and Home tasks"
      ((agenda "" ((org-agenda-span 1)))
       (todo "IN-PROGRESS")
       (todo "WAITING")
       (tags-todo "inbox|break")
       (todo "NEXT"))
      ((org-agenda-sorting-strategy '(time-up habit-up priority-down category-up))))

     ("i" "In-Progress Tasks"
      ((todo "IN-PROGRESS|WAITING")
       (agenda ""))
      ((org-agenda-sorting-strategy '(time-up habit-up priority-down category-up))))

     ("g" "Goals: 12 Week Year"
      ((agenda "")
       (todo "IN-PROGRESS|WAITING"))
      ((org-agenda-sorting-strategy '(time-up habit-up priority-down category-up))
       (org-agenda-tag-filter-preset '("+12WY"))
       (org-agenda-start-with-log-mode '(closed clock state))
       (org-agenda-archives-mode t)
       ))

     ("r" "Weekly Review"
      ((agenda "" ((org-agenda-span 14)))
       (todo))
      ((org-agenda-sorting-strategy '(time-up habit-up category-up priority-down ))
       (org-agenda-files "~/.emacs.d/org/weekly-reivew-agenda-files.org")
       ;; (org-agenda-include-diary nil)
       ))))
  :init
  ;; Originally from here: https://stackoverflow.com/a/59001859/2178312
  (defun gopar/get-schedule-or-deadline-if-available ()
    (let ((scheduled (org-get-scheduled-time (point)))
          (deadline (org-get-deadline-time (point))))
      (if (not (or scheduled deadline))
          (format "🗓️ ")
        "   "))))

Org Capture

(use-package org-capture
  :bind (("C-c c" . org-capture))
  :commands org-capture
  :custom
  ;; dont create a bookmark when calling org-capture
  (org-capture-bookmark nil)
  ;; also don't create bookmark in other things
  (org-bookmark-names-plist nil)
  (org-capture-templates
   '(
     ("c" "Inbox" entry (file "~/.emacs.d/org/private/org-roam/gtd/inbox.org")
      "* TODO %?\n:PROPERTIES:\n:DATE_ADDED: %u\n:END:")
     ("p" "Project" entry (file "~/.emacs.d/org/private/org-roam/gtd/gtd.org")
      "* %? [%] :project: \n:PROPERTIES: \n:TRIGGER: next-sibling todo!(NEXT) scheduled!(copy)\n:ORDERED: t \n:DATE_ADDED: %u\n:END:\n** TODO Add entry")
     ("t" "Tickler" entry (file "~/.emacs.d/org/private/org-roam/gtd/tickler.org")
      "* TODO %? \nSCHEDULED: %^{Schedule}t\n:PROPERTIES:\n:DATE_ADDED: %u\n:END:\n")
     ("k" "Contact" entry (file "~/.emacs.d/org/private/org-roam/references/contacts.org")
      "* %? \n%U
:PROPERTIES:
:EMAIL:
:PHONE:
:NICKNAME:
:NOTE:
:ADDRESS:
:BIRTHDAY:
:Blog:
:END:"))))

Org OL

(use-package ol
  :after org
  :custom
  (org-link-shell-confirm-function 'y-or-n-p)
  (org-link-elisp-confirm-function 'y-or-n-p))

Org Src

(use-package org-src
  :after org
  :custom
  (org-src-preserve-indentation nil)
  ;; Don't ask if we already have an open Edit buffer
  (org-src-ask-before-returning-to-edit-buffer nil)
  (org-edit-src-content-indentation 0))

Ob Core

(use-package ob-core
  :after org
  :custom
  ;; Don't ask every time when I run a code block
  (org-confirm-babel-evaluate nil))

Org Habit

(use-package org-habit
  :after org
  :custom
  (org-habit-graph-column 45))

Org indent

(use-package org-indent
  :ensure nil
  :after org
  :diminish
  :custom
  (org-indent-mode-turns-on-hiding-stars nil))

I know this isn’t built in but putting it here w/ org mode stuff

(use-package org-pomodoro
  :ensure t
  :after org
  :bind (("<f12>" . org-pomodoro))
  :hook ((org-pomodoro-started . gopar/load-window-config-and-close-home-agenda)
         (org-pomodoro-finished . gopar/save-window-config-and-show-home-agenda))
  :custom
  (org-pomodoro-manual-break t)
  (org-pomodoro-short-break-length 20)
  (org-pomodoro-long-break-length 30)
  (org-pomodoro-length 60)
  :init
  (defun gopar/home-pomodoro ()
    (interactive)
    (setopt org-pomodoro-length 25
          org-pomodoro-short-break-length 5))

  (defun gopar/work-pomodoro ()
    (interactive)
    (setopt org-pomodoro-length 60
          org-pomodoro-short-break-length 20))

  (defun gopar/save-window-config-and-show-home-agenda ()
    (interactive)
    (window-configuration-to-register ?`)
    (delete-other-windows)
    (org-save-all-org-buffers)
    (org-agenda nil "h"))

  (defun gopar/load-window-config-and-close-home-agenda ()
    (interactive)
    (org-save-all-org-buffers)
    (shell-command "shortcuts run 'Emacs Pomodoro'")
    (jump-to-register ?`)))
(use-package org-roam
  :ensure t
  :defer
  ;; :after org
  ;; :hook (org-mode . org-roam-db-autosync-mode)
  :commands (org-roam-node-find)
  :custom
  (org-roam-directory (expand-file-name "~/.emacs.d/org/private/org-roam"))
  (org-roam-db-location (expand-file-name "~/.emacs.d/org/private/org-roam.db"))
  (org-roam-capture-templates
   '(("d" "default" plain "%?"
      :target (file+head "./references/${slug}.org" "#+title: ${title}\n")
      :unnarrowed t)))
  (org-roam-dailies-directory (expand-file-name "~/.emacs.d/org/private/journal/"))
  (org-roam-dailies-capture-templates
   `(("d" "daily" plain (file "/Users/gopar/.emacs.d/org/templates/dailies-daily.template")
      :target (file+head "daily/%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))

     ("w" "weekly" plain (file "/Users/gopar/.emacs.d/org/templates/dailies-weekly.template")
      :target (file+head "weekly/%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))

     ("m" "monthly" plain (file "/Users/gopar/.emacs.d/org/templates/dailies-monthly.template")
      :target (file+head "monthly/%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))

  :bind (:map global-map
              (("C-c n i" . org-roam-node-insert)
               ("C-c n f" . org-roam-node-find)
               ("C-c n g" . org-roam-graph)
               ("C-c n n" . org-roam-capture)
               ("C-c n d" . org-roam-dailies-capture-today)
               ("C-c n s" . consult-org-roam-search))))
(use-package org-modern
  :ensure t
  :after org
  :custom
  (org-modern-hide-stars nil)
  (org-modern-fold-stars '(("" . "") ("" . "") ("" . "") ("" . "") ("" . "")))
  :hook
  (org-mode . org-modern-mode)
  (org-agenda-finalize . org-modern-agenda))

Org Modern Indent

(use-package org-modern-indent
  :ensure t
  :after org
  :vc (:url "https://github.com/jdtsmith/org-modern-indent/" :rev :newsest)
  :init
  (add-hook 'org-mode-hook #'org-modern-indent-mode 90))

Org Annotate File

;; Belongs from the org-contrib pkg?
(use-package org-annotate-file
  :ensure nil
  :load-path "lisp/org"
  :defer
  ;; :hook (prog-mode)
  :custom
  (org-annotate-file-add-search t)
  (org-annotate-file-storage-file (concat user-emacs-directory "var/.org-annotate-file.org"))
  :bind (:map prog-mode-map
              ("C-c C-s" . gopar/org-annotate-file)
         :map python-mode-map
              ("C-c C-s" . gopar/org-annotate-file)
         :map python-ts-mode-map
              ("C-c C-s" . gopar/org-annotate-file)
         :map web-mode-map
              ("C-c C-s" . gopar/org-annotate-file))
  :init
  (defun gopar/org-annotate-file (&optional arg)
    "Annotate current line.
When called with a prefix aurgument, it will open annotations file."
    (interactive "P")
    (require 'org-annotate-file)
    (let* ((root (projectile-project-root))
           (org-annotate-file-storage-file
            (if root
                (format "%s.org-annotate.org" root)
              org-annotate-file-storage-file)))
      (if arg
          (find-file org-annotate-file-storage-file)
        (org-annotate-file)))))

Org Misc

(defun gopar/daily-log ()
  "Insert a new daily log entry with the current date."
  (interactive)
  (goto-char (point-max))
  (org-insert-heading-respect-content)
  (insert (format-time-string "[%Y-%m-%d %a]") "\n")
  (insert "- Accomplishments:\n")
  (insert "  - Task 1\n")
  (insert "  - Task 2\n")
  (insert "- Challenges:\n")
  (insert "  - Issue 1\n")
  (insert "  - Issue 2\n")
  (insert "- Learnings:\n")
  (insert "  - Insight 1\n")
  (insert "  - Insight 2\n")
  (insert "- Plans for Tomorrow:\n")
  (insert "  - Task 1\n")
  (insert "  - Task 2\n"))

(defalias 'gopar/journal-eng-entry 'gopar/daily-log)

Eshell

Some of the following are stolen from https://github.com/manateelazycat/aweshell

(use-package eshell
  :ensure nil
  :defer t
  :hook ((eshell-directory-change . gopar/sync-dir-in-buffer-name)
         (eshell-mode . gopar/eshell-specific-outline-regexp)
         (eshell-mode . gopar/eshell-setup-keybinding)
         (eshell-banner-load . (lambda ()
                                 (setopt eshell-banner-message
                                       (concat (shell-command-to-string "fortune -s | cowsay") "\n\n"))))
         (eshell-mode . (lambda ()
                          (setq-local completion-styles '(basic)) ; maybe emacs21?
                          (setq-local corfu-count 10)
                          (setq-local corfu-auto nil)
                          (setq-local corfu-preview-current nil)
                          (setq-local completion-at-point-functions '(pcomplete-completions-at-point cape-file)))))
  :custom
  (eshell-scroll-to-bottom-on-input t)
  (eshell-highlight-prompt t)
  (eshell-history-size 1024)
  (eshell-hist-ignoredups t)
  (eshell-input-filter 'gopar/eshell-input-filter)
  (eshell-cd-on-directory t)
  (eshell-list-files-after-cd nil)
  (eshell-pushd-dunique t)
  (eshell-last-dir-unique t)
  (eshell-last-dir-ring-size 32)
  :config
  (add-to-list 'eshell-modules-list 'eshell-elecslash)
  (advice-add #'eshell-add-input-to-history
                :around
                #'gopar/adviced-eshell-add-input-to-history)

  :init
  (defun gopar/eshell-setup-keybinding ()
    ;; Workaround since bind doesn't work w/ eshell??
    (define-key eshell-mode-map (kbd "C-c >") 'gopar/eshell-redirect-to-buffer)
    (define-key eshell-hist-mode-map (kbd "M-r") 'consult-history))

  (defun gopar/adviced-eshell-add-input-to-history (orig-fun &rest r)
      "Cd to relative paths aren't that useful in history. Change to absolute paths."
      (require 'seq)
      (let* ((input (nth 0 r))
             (args (progn
                     (set-text-properties 0 (length input) nil input)
                     (split-string input))))
        (if (and (equal "cd" (nth 0 args))
                 (not (seq-find (lambda (item)
                                  ;; Don't rewrite "cd /ssh:" in history.
                                  (string-prefix-p "/ssh:" item))
                                args))
                 (not (seq-find (lambda (item)
                                  ;; Don't rewrite "cd -" in history.
                                  (string-equal "-" item))
                                args)))
            (apply orig-fun (list (format "cd %s"
                                          (expand-file-name (concat default-directory
                                                                    (nth 1 args))))))
          (apply orig-fun r))))

  (defun gopar/eshell-input-filter (input)
    "Do not save on the following:
       - empty lines
       - commands that start with a space, `ls`/`l`/`lsd`"
    (and
     (eshell-input-filter-default input)
     (eshell-input-filter-initial-space input)
     (not (string-prefix-p "ls " input))
     (not (string-prefix-p "lsd " input))
     (not (string-prefix-p "l " input))))

  (defun eshell/cat-with-syntax-highlighting (filename)
    "Like cat(1) but with syntax highlighting.
Stole from aweshell"
    (let ((existing-buffer (get-file-buffer filename))
          (buffer (find-file-noselect filename)))
      (eshell-print
       (with-current-buffer buffer
         (if (fboundp 'font-lock-ensure)
             (font-lock-ensure)
           (with-no-warnings
             (font-lock-fontify-buffer)))
         (let ((contents (buffer-string)))
           (remove-text-properties 0 (length contents) '(read-only nil) contents)
           contents)))
      (unless existing-buffer
        (kill-buffer buffer))
      nil))
  (advice-add 'eshell/cat :override #'eshell/cat-with-syntax-highlighting)

  (defun gopar/sync-dir-in-buffer-name ()
    "Update eshell buffer to show directory path.
Stolen from aweshell."
    (let* ((root (projectile-project-root))
           (root-name (projectile-project-name root)))
      (if root-name
          (rename-buffer (format "*eshell %s* %s" root-name (s-chop-prefix root default-directory)) t)
        (rename-buffer (format "*eshell %s*" default-directory) t))))

  (defun gopar/eshell-redirect-to-buffer (buffer)
    "Auto create command for redirecting to buffer."
    (interactive (list (read-buffer "Redirect to buffer: ")))
    (insert (format " >>> #<%s>" buffer)))

(defun gopar/eshell-specific-outline-regexp ()
  (setq-local outline-regexp eshell-prompt-regexp)))
(use-package eshell-syntax-highlighting
  :ensure t
  :after eshell
  :hook (eshell-first-time-mode . eshell-syntax-highlighting-global-mode)
  :init
  (defface eshell-syntax-highlighting-invalid-face
    '((t :inherit diff-error))
    "Face used for invalid Eshell commands."
    :group 'eshell-syntax-highlighting))
(use-package eshell-git-prompt
  :after eshell
  :ensure t)

(use-package powerline-with-venv
  :ensure nil
  :after eshell-git-prompt
  :load-path "lisp/themes/powerline-with-venv"
  :config
  (add-to-list 'eshell-git-prompt-themes
               '(powerline-plus eshell-git-prompt-powerline-venv eshell-git-prompt-powerline-regexp))
  (eshell-git-prompt-use-theme 'powerline-plus))
(use-package capf-autosuggest
  :ensure t
  :hook ((eshell-mode . capf-autosuggest-mode))
  :custom
  (capf-autosuggest-dwim-next-line nil))
(use-package eshell-bookmark
  :ensure t
  :after eshell
  :hook (eshell-mode . eshell-bookmark-setup))

Tramp

(use-package tramp
  :ensure nil
  :defer t
  :custom
  (tramp-histfile-override nil))

Python

Run on every fresh virtualenv install pip install jedi epc importmagic ruff mypy coverage pytest-cov pytest pyright

I get some weird auto completion in inferior python shell mode when I leave the default completion function. Lets just have in buffer completion

(use-package python
  :ensure nil
  :bind (:map python-mode-map
              ("C-c C-p" . nil)
              ("C-c C-e" . nil)
              ("C-c C-s" . nil)
              ("C-c C-z" . gopar/run-python)
         :map python-ts-mode-map
              ("C-c C-p" . nil)
              ("C-c C-e" . nil)
              ("C-c C-s" . nil)
              ("C-c C-z" . gopar/run-python))
  :hook ((python-ts-mode . (lambda ()
                          (setq-local forward-sexp-function nil)
                          (make-local-variable 'python-shell-virtualenv-root)
                          (setq-local comment-inline-offset 2)
                          (setq-local completion-at-point-functions
                                      '(cape-file
                                        ;; python-completion-at-point
                                        gopar/cape-yasnippet-keyword-dabbrev
                                        gopar/cape-dict-only-in-strings
                                        gopar/cape-dict-only-in-comments
                                        ))))
         (inferior-python-mode . (lambda ()
                                   (setq-local completion-at-point-functions '(t)))))

  :init
  (defun gopar/run-python ()
    "Wrapper function for `run-python` that checks if the current project is a Django project."
    (interactive)
    (let* ((manage-directory (locate-dominating-file default-directory "manage.py"))
           (default-directory (or manage-directory default-directory)))
      (if manage-directory
          (run-python (format "%s manage.py shell_plus" (python-shell-calculate-command) manage-directory) python-shell-dedicated 0)
        (run-python (python-shell-calculate-command) python-shell-dedicated 0))))
  :custom
  (python-shell-dedicated 'project)
  (python-shell-interpreter "python")
  (python-shell-interpreter-args "")
  (python-forward-sexp-function nil)
  (python-shell-completion-native-disabled-interpreters '("python" "pypy")))
(use-package ruff-format
  :ensure t
  :defer
  :hook ((python-mode python-ts-mode) . gopar/enable-ruff-if-found)
  :init
  (defun gopar/enable-ruff-if-found ()
    "Format the current buffer using the 'ruff` program, if available."
    (interactive)
    (if (executable-find "ruff")
        (ruff-format-on-save-mode))))

Required Jedi to work properly

(use-package pydoc
  :ensure t
  :defer
  :bind (:map python-mode-map
              ("C-c C-d" . gopar/pydoc-at-point))
  :init
  (add-to-list 'display-buffer-alist
            '("^\\*pydoc" display-buffer-in-side-window
              (slot . 1)
              (side . right)
              (window-parameters . ((no-delete-other-windows . t)))
              (dedicated . t)
              ;; (window-width . 80)
              ))

  (defun gopar/pydoc-at-point ()
    "Display pydoc in a dedicated window.
Calling `gopar/pydoc-at-point' displays the pydoc in a new dedicated window.
Calling `C-u gopar/pydoc-at-point' closes the dedicated window."
    (interactive)
    (let ((default-directory (file-name-directory (buffer-file-name))))
      (if (not (eq current-prefix-arg nil))
          (when (get-buffer-window "*pydoc*")
            (delete-window (get-buffer-window "*pydoc*")))
        (pydoc-at-point)
        (set-window-dedicated-p (get-buffer-window "*pydoc*") t)))))

Pip Requirements

(use-package pip-requirements
  :ensure t
  :defer
  :hook (pip-requirements-mode . (lambda () (focus-mode -1)))
  )

UV

(use-package uv-mode
  :ensure
  :bind (:map uv-mode-map
              ("C-c C-s" . nil)
              ("C-c C-u" . nil))
  :hook (python-base-mode . uv-mode-auto-activate-hook))

Run python repl in a vterm process

(use-package py-vterm-interaction
  :hook (python-base-mode . py-vterm-interaction-mode)
  :config
  (setq-default py-vterm-interaction-repl-program "ipython")
  (setq-default py-vterm-interaction-silent-cells t)
  )

C++ (cpp)

(use-package c-ts-mode
  :ensure t
  :defer
  :hook ((c-ts-base-mode . eglot-ensure)
         (c-ts-base-mode . flycheck-mode)
         (c-ts-base-mode . clang-format-on-save-mode)
         (c++-ts-mode . (lambda () (setq-local flycheck-clang-language-standard "c++20")))
         )
  :init
  (defun gopar/easy-underscore-for-C (arg)
    "In C/C++ mode: type '_' instead of ';'.
If the previous character was '_', replace it with a semicolon.
With prefix ARG, insert a literal semicolon."
    (interactive "P")
    (if arg
        (insert ";")
      (if (and (memq major-mode '(c++-mode c-mode c++-ts-mode c-ts-mode))
               (not (bobp))
               (eq (char-before) ?_))
          (progn
            (delete-char -1)
            (insert ";"))
        (insert "_"))))

  (defun gopar/easy-smart-dash ()
    "Press - once for '-', twice for '->', three times for '--'."
    (interactive)
    (let ((prev (char-before))
          (prev2 (char-before (1- (point)))))
      (cond
       ;; if previous is '-' and before that is '-', change to "--"
       ((and prev prev2 (eq prev ?>) (eq prev2 ?-))
        (delete-char -1)
        (insert "-"))
       ;; if previous is '-', then add '>' to make '->'
       ((and prev (eq prev ?-))
        (insert ">"))
       ;; otherwise, just insert '-'
       (t
        (insert "-")))))

  :bind (:map c-ts-base-mode-map
              (";" . gopar/easy-underscore-for-C)
              ("-" . gopar/easy-smart-dash)))

Cmake

(use-package cmake-mode
  :ensure
  :defer)

Kotlin

(use-package kotlin-mode :ensure t :defer)

Tree Sitter

(use-package treesit
  :ensure nil
  :defer
  :custom
  (treesit-font-lock-level 2)
  :init
  (defun gopar/treesit-font-lock-level-cycle (arg)
    (interactive "P")
    (let ((level (if arg
                     (read-number "Set treesit font-lock level (1-4): " 1)
                   (1+ treesit-font-lock-level))))
      (when (> level 4)
        (setq level 1))
      (setopt treesit-font-lock-level level)
      (message "treesit-font-lock-level = %d" level))))

Tree Sitter Auto Magic

(use-package treesit-auto
  :ensure t
  :hook (emacs-startup . global-treesit-auto-mode)
  :custom
  (treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all))

Flycheck Kotlin

(use-package flycheck-kotlin
  :ensure t
  :defer
  :hook (kotlin-mode . (lambda () (flycheck-mode 1) (flycheck-kotlin-setup))))

GUD (Debugger)

(use-package gud
  :ensure nil
  :defer
  :custom
  (gud-pdb-command-name "PYTHONBREAKPOINT=pdb.set_trace python -m pdb"))

Compile

(use-package compile
  :ensure nil
  :defer
  :custom
  (compilation-scroll-output 'first-error)
  (compilation-ask-about-save nil)
  (compilation-always-kill t)
  (compilation-max-output-line-length nil)
  (compilation-buffer-name-function 'gopar/compilation-buffer-name-function)
  :hook (compilation-mode . hl-line-mode)
  :config
  (defun gopar/compilation-buffer-name-function (compilation-mode)
    "Rename buffer to whatever command was used.
eg. *python main.py*"
    (concat "*" (downcase compilation-mode)
          (when (projectile-project-p) (concat " " (projectile-project-name))) "* "
          compile-command))

  ; from enberg on #emacs
  (add-hook 'compilation-finish-functions
            (lambda (buf str)
              (if (null (string-match ".*exited abnormally.*" str))
                  ;;no errors, make the compilation window go away in a few seconds
                  (progn
                    (run-at-time
                     "1 sec" nil 'delete-windows-on
                     (buffer-name))
                    (message "No Compilation Errors!"))))))
(use-package fancy-compilation
  :ensure t
  :after compile
  :config
  (fancy-compilation-mode)
  :custom
  (fancy-compilation-override-colors nil)
  (fancy-compilation-scroll-output 'first-error))

For TDD development

(use-package recompile-on-save
  :ensure t
  :after compile
  :config
  (recompile-on-save-advice compile)
  :init
  ;; Hide the buffer message that pops up after running advice on compile
  (add-to-list 'display-buffer-alist
             '("^\\*Compile-Log"
               (display-buffer-no-window)
               (allow-no-window . t))))

Winner

Window Management

(use-package winner
  :ensure nil
  :commands (winner-undo winner-redo)
  :hook emacs-startup
  :custom
  (winner-boring-buffers '("*Completions*" "*Help*" "*Apropos*"
                           "*Buffer List*" "*info*" "*Compile-Log*")))

Window

(use-package window
  :ensure nil
  :defer
  :custom
  (recenter-positions '(middle top bottom)))

Midnight

(use-package midnight
  :ensure nil
  :defer 30
  :custom
  (clean-buffer-list-delay-general 0)
  (clean-buffer-list-delay-special 0)
  (clean-buffer-list-kill-regexps '("\\`\\*Man " "\\`\\*helpful" "\\`\\magit")))

Executeable

(use-package executable
  :ensure nil
  :defer
  :hook (after-save . executable-make-buffer-file-executable-if-script-p))

Jinx Spelling

If on new system, might need to install enchant and pkg-config

(use-package jinx
  :ensure t
  :defer
  :hook (after-init . global-jinx-mode)
  :bind (("C-." . jinx-correct)
         ("C-," . jinx-next)
         :map jinx-mode-map
         ("M-$" . nill)
         ))

Dictionary

Look up word at point using dict.org in readme/text/org-mode buffers

(use-package dictionary
  :defer
  :ensure nil
  :bind (:map text-mode-map
              ("M-." . dictionary-lookup-definition)
         :map org-mode-map
              ("M-." . dictionary-lookup-definition)
         :map dictionary-mode-map
              ("M-." . dictionary-lookup-definition)
         :map eww-mode-map
         ("M-." . dictionary-lookup-definition))
  :init
  (add-to-list 'display-buffer-alist
               '("^\\*Dictionary\\*" display-buffer-in-side-window
                 (side . left)
                 (window-width . 50)))
  :custom
  (dictionary-server "dict.org"))

Minibuffer

(use-package minibuffer
  :ensure nil
  :defer t)

Time

(use-package time
  :ensure nil
  :defer t
  :custom
  (world-clock-time-format "%A %d %B %r %Z")
  (display-time-day-and-date t)
  (display-time-default-load-average nil)
  (display-time-mail-string "")
  (zoneinfo-style-world-list
  '(("America/Los_Angeles" "Sacramento, CA")
    ("America/New_York" "Buffalo, NY")
    ("America/Chicago" "Tulsa, OK")
    ("America/Chicago" "Chicago, IL")
    ("Asia/Tokyo" "Tokyo")
    ("Europe/Madrid" "Barcelona"))))

Proced

(use-package proced
  :ensure nil
  :defer t
  :custom
  (proced-enable-color-flag t)
  (proced-tree-flag t))

Browse URL

(use-package browse-url
  :ensure nil
  :defer t
  :custom
  ;; Emacs can't find browser binaries
  (browse-url-chrome-program "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")
  (browse-url-firefox-program "/Applications/Firefox.app/Contents/MacOS/firefox")
  ;; Neat trick to open that route to different places
  (browse-url-firefox-new-window-is-tab t)
  ;; Default to using eww for browsing
  (browse-url-browser-function #'eww-browse-url)
  (browse-url-handlers '(;; Work urls
                         ("teams\\.microsoft\\.com"  . browse-url-chrome)
                         ("outlook\\.office365\\.com" . browse-url-chrome)
                         (".*\\.atlassian\\.net" . browse-url-chrome)
                         ;; Anything with summit will be considered work related
                         ("summit" . browse-url-chrome)
                         ;; Personal urls
                         (".*youtube\\.com"        . browse-url-firefox)
                         (".*chatgpt\\.com"         . browse-url-firefox)
                         (".*"                    . eww-browse-url)))
  :config
  (put 'browse-url-handlers 'safe-local-variable (lambda (x) t))
  (put 'browse-url-browser-function 'safe-local-variable (lambda (x) t)))

Eww

(use-package eww
  :defer t
  :hook (eww-after-render . shrface-mode)
  :custom
  (eww-auto-rename-buffer 'title)
  (eww-browse-url-new-window-is-tab nil)
  :config
  (require 'shrface))

Ewnium

(use-package ewnium
  :ensure nil
  :defer
  :load-path "lisp/eww"
  :hook (eww-mode . ewnium-mode))

SHR

(use-package shrface
  :ensure t
  :defer t
  :bind (:map eww-mode-map
              ("M-g o" . shrface-headline-consult)
              ("C-c C-o" . shrface-occur))
  :config
  (shrface-basic)
  (shrface-trial)
  ;; (shrface-default-keybindings)
  (setopt shrface-href-versatile t))
(use-package shr-tag-pre-highlight
  :ensure t
  :after shr
  :config
  (add-to-list 'shr-external-rendering-functions
               '(pre . shr-tag-pre-highlight)))

Prog Mode

(use-package prog-mode
  :ensure nil
  :defer
  :hook ((prog-mode . subword-mode)
         (prog-mode . hl-line-mode)
         (prog-mode . (lambda () (setq-local fill-column 120)))))

Which Function

(use-package which-func
  :ensure nil
  :defer
  :hook (prog-mode . which-function-mode))

Projectile

(use-package projectile
  :ensure
  :commands (projectile-project-root projectile-switch-project)
  :hook (after-init . projectile-mode)
  :bind-keymap
  ("C-c p" . projectile-command-map)
  :bind (:map projectile-command-map
              ("x a" . gopar/projectile-run-aider)
              ("b" . consult-project-buffer)
              ("s r" . consult-ripgrep)
              ("s g" . consult-grep))

  :custom
  (projectile-indexing-method 'hybrid)  ;; Not sure if this still needed?
  (projectile-per-project-compilation-buffer nil)
  (projectile-git-submodule-command nil)
  :config
  (setq frame-title-format '(:eval (if (projectile-project-root) (projectile-project-root) "%b")))
  (advice-add 'projectile--run-project-cmd :override #'gopar/projectile--run-project-cmd)

  (defun gopar/projectile-run-aider (&optional arg)
    (interactive "P")
    (gopar/projectile--aider arg t))

  (defun gopar/projectile--aider (&optional new-process other-window)
    "Invoke `aider' in the project's root.

Use argument NEW-PROCESS to indicate creation of a new process instead.
Use argument OTHER-WINDOW to indentation whether the buffer should
be displayed in a different window.

Switch to the project specific term buffer if it already exists."
    (let* ((project (projectile-acquire-root))
           (buffer (projectile-generate-process-name "aider" new-process project))
           (vterm-buffer-name-string nil))
      (unless (require 'vterm nil 'noerror)
        (error "Package 'vterm' is not available"))
      (if (buffer-live-p (get-buffer buffer))
          (if other-window
              (switch-to-buffer-other-window buffer)
            (switch-to-buffer buffer))
        (projectile-with-default-dir project
          (if other-window
              (vterm-other-window buffer)
            (vterm buffer))

          (vterm-send-string
           (mapconcat 'identity
                      `("aider"
                        "--architect"
                        "--model"
                        "claude-3-7-sonnet-latest"
                        "--editor-model"
                        "claude-3-5-sonnet-latest"
                        "--weak-model"
                        "claude-3-5-haiku-latest"
                        ;; "--no-auto-accept-architect"
                        "--analytics-disable"
                        "--no-auto-commits"
                        "--no-dirty-commits"
                        "--no-attribute-author"
                        "--no-attribute-committer"
                        "--no-auto-lint"
                        ;; "--cache-prompts"
                        ;; "--no-stream"
                        "--watch-files"
                        "--notifications"
                        "--notifications-command \"say 'Aider is ready'\""
                        ,(if (file-exists-p (concat project "CONVENTIONS.md"))
                            (concat "--read " project "CONVENTIONS.md")
                          ""))
                      " "))
        (vterm-send-return)))))

  ;; :init
  ;; Redefinig with my changes since projectil overwrites `compilation-buffer-name-function`
  (cl-defun gopar/projectile--run-project-cmd
      (command command-map &key show-prompt prompt-prefix save-buffers use-comint-mode)
    "Run a project COMMAND, typically a test- or compile command.

Cache the COMMAND for later use inside the hash-table COMMAND-MAP.

Normally you'll be prompted for a compilation command, unless
variable `compilation-read-command'.  You can force the prompt
by setting SHOW-PROMPT.  The prompt will be prefixed with PROMPT-PREFIX.

If SAVE-BUFFERS is non-nil save all projectile buffers before
running the command.

The command actually run is returned."
    (let* ((project-root (projectile-project-root))
           (default-directory (projectile-compilation-dir))
           (command (projectile-maybe-read-command show-prompt
                                                   command
                                                   prompt-prefix)))
      (when command-map
        (puthash default-directory command command-map)
        (let ((hist (projectile--get-command-history project-root)))
          (cond
           ((eq projectile-cmd-hist-ignoredups t)
            (unless (string= (car-safe (ring-elements hist)) command)
              (ring-insert hist command)))
           ((eq projectile-cmd-hist-ignoredups 'erase)
            (let ((idx (ring-member hist command)))
              (while idx
                (ring-remove hist idx)
                (setq idx (ring-member hist command))))
            (ring-insert hist command))
           (t (ring-insert hist command)))))
      (when save-buffers
        (save-some-buffers (not compilation-ask-about-save)
                           (lambda ()
                             (projectile-project-buffer-p (current-buffer)
                                                          project-root))))
      (when projectile-per-project-compilation-buffer
        (setq compilation-buffer-name-function #'projectile-compilation-buffer-name)
        (setq compilation-save-buffers-predicate #'projectile-current-project-buffer-p))
      (unless (file-directory-p default-directory)
        (mkdir default-directory))
      (projectile-run-compilation command use-comint-mode)
      command))
  )

Repeat Mode

Allows repeating via `C-x z` (pressing z multiple times keeps repeating) or by pressing last keybinding of previous command

(use-package repeat
  :ensure nil
  :hook (after-init . repeat-mode)
  :custom
  (repeat-too-dangerous '(kill-this-buffer))
  (repeat-exit-timeout 5))

Save Place

(use-package saveplace
  :ensure nil
  :hook (after-init . save-place-mode))

Save History

(use-package savehist
  :ensure nil
  :hook (after-init . savehist-mode)
  :custom
  (savehist-additional-variables '(abbrev-minor-mode-table-alist)))

Grep

(use-package grep
  :ensure nil
  :defer
  :config
  (setopt grep-find-ignored-directories (append grep-find-ignored-directories '(".mypy_cache" ".pytest_cache" "htmlcov"))))

ripgrep (rg)

(use-package rg
  :ensure t
  :defer
  :hook (rg-mode . rg-save-search)
  :config
  (rg-enable-menu))

wgrep

(use-package wgrep-ag :ensure t :defer)

Code Completion

A collection of packages that act as ‘smart’ completion in which really are not :) Also includes displaying of them

(use-package vertico
  :ensure t
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)
  :init
  (vertico-mode)
  (setopt vertico-cycle t))

(use-package vertico-multiform
  :ensure nil
  :hook (after-init . vertico-multiform-mode)
  :init
  (setopt vertico-multiform-commands
          '((consult-line (:not posframe))
            (consult-xref (:not posframe))
            (gopar/consult-line (:not posframe))
            (consult-line-thing-at-point (:not posframe))
            (consult-ag (:not posframe))
            (consult-ripgrep (:not posframe))
            (consult-grep (:not posframe))
            (consult-imenu (:not posframe))
            (consult-outline (:not posframe))
            (consult-yank-pop (:not posframe))
            (consult-imenu-multi (:not posframe))
            (consult-yasnippet (:not posframe))
            (t posframe))))

;; just for looks
(use-package vertico-posframe
  :ensure t
  :hook (after-init . vertico-posframe-mode)
  :custom
  (vertico-posframe-poshandler 'posframe-poshandler-frame-top-center)
  (vertico-posframe-parameters
   '((left-fringe . 8)
     (right-fringe . 8))))

(use-package dabbrev
  :defer t
  :custom
  (dabbrev-upcase-means-case-search t)
  (dabbrev-check-all-buffers nil)
  (dabbrev-check-other-buffers t)
  (dabbrev-friend-buffer-function 'dabbrev--same-major-mode-p)
  (dabbrev-ignored-buffer-regexps '("\\.\\(?:pdf\\|jpe?g\\|png\\)\\'")))

(use-package corfu
  :ensure t
  ;; Originally, I liked the idea of `corfu-send` but this makes it behave
  ;; in way that is different from 'fish' shell. So lets disable and see
  ;; how we feel about it in the future
  ;; :bind (:map corfu-map
  ;;             ("RET" . corfu-send))
  :custom
  (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
  (corfu-auto t)                 ;; Enable auto completion
  (corfu-on-exact-match 'insert) ;; Insert when there's only one match
  (corfu-quit-no-match t)        ;; Quit when ther is no match
  :init
  (global-corfu-mode)

  (defun corfu-enable-always-in-minibuffer ()
    "Enable Corfu in the minibuffer if Vertico/Mct are not active."
    (unless (or (bound-and-true-p mct--active)
                (bound-and-true-p vertico--input)
                (eq (current-local-map) read-passwd-map))
      ;; (setq-local corfu-auto nil) ;; Enable/disable auto completion
      (setq-local corfu-echo-delay nil ;; Disable automatic echo and popup
                  corfu-popupinfo-delay nil)
      (corfu-mode 1)))

  (add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1))

(use-package cape
  :ensure t
  :bind ("C-c SPC" . cape-dabbrev)
  :custom
  (cape-dict-case-replace nil)
  (cape-dabbrev-buffer-function 'cape-same-mode-buffers)

  :init
  (defun gopar/cape-dict-only-in-comments ()
    (cape-wrap-inside-comment 'cape-dict))

  (defun gopar/cape-dict-only-in-strings ()
    (cape-wrap-inside-string 'cape-dict))

  (defun gopar/cape-yasnippet-keyword-dabbrev ()
    (cape-wrap-super #'yasnippet-capf #'cape-keyword #'cape-dabbrev))

  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'gopar/cape-yasnippet-keyword-dabbrev)
  (add-to-list 'completion-at-point-functions #'gopar/cape-dict-only-in-strings)
  (add-to-list 'completion-at-point-functions #'gopar/cape-dict-only-in-comments))

(use-package orderless
  :ensure t
  :after consult
  :custom
  (completion-styles '(orderless basic initials flex))
  (completion-category-overrides '((file (styles basic partial-completion)))))

(use-package consult
  :ensure
  ;; :commands (gopar/consult-line consult-buffer consult-bookmark consult-theme consult-line-thing-at-point)
  :bind (("C-s" . gopar/consult-line)
         ("C-c m" . consult-man)
         ("C-c i" . consult-info)
         ("C-c M-x" . consult-mode-command)
         ;; C-x bindings in `ctl-x-map'
         ("C-x b" . consult-buffer)                ;; orig. switch-to-buffer
         ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
         ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
         ("C-x r b" . consult-bookmark)
         ("C-x r s" . consult-register-store)
         ("C-x r l" . consult-register-load)
         ("C-x r i" . consult-register)

         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)

         ;; M-g bindings in `goto-map'
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flycheck)               ;; Alternative: consult-flycheck
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)

         ;; M-s bindings in `search-map'
         ("M-s d" . consult-find)
         ("M-s c" . consult-locate)
         ("M-s g" . consult-grep)
         ("M-s G" . consult-git-grep)
         ("M-s r" . consult-ripgrep)
         ("M-s l" . consult-line)
         ("M-s L" . consult-line-multi)
         ("M-s k" . consult-keep-lines)
         ("M-s f" . consult-focus-lines)

         ;; Random bindings
         ("C-z" . consult-theme)
         ("M-y" . consult-yank-pop)

         ;; Isearch integration
         ("M-s e" . consult-isearch-history)
         :map isearch-mode-map
         ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
         ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
         ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
         ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch

         :map minibuffer-local-map
         ("M-s" . consult-history)                 ;; orig. next-matching-history-element
         ("M-r" . consult-history))

  :config
  (setopt consult-project-function (lambda (_) (projectile-project-root)))
  (setopt xref-show-xrefs-function #'consult-xref
          xref-show-definitions-function #'consult-xref)
  (setopt consult-narrow-key "<")
  (setopt consult-line-start-from-top nil)
  (consult-customize
   consult-line
   :add-history (seq-some #'thing-at-point '(region symbol)))

  (defalias 'consult-line-thing-at-point 'consult-line)

  (consult-customize
   consult-line-thing-at-point
   :initial (thing-at-point 'symbol))

  (defun gopar/consult-line (&optional arg)
    "Start consult search with selected region if any.
If used with a prefix, it will search all buffers as well."
    (interactive "p")
    (let ((cmd (if current-prefix-arg '(lambda (arg) (consult-line-multi t arg)) 'consult-line)))
      (if (use-region-p)
          (let ((regionp (buffer-substring-no-properties (region-beginning) (region-end))))
            (deactivate-mark)
            (funcall cmd regionp))
        (funcall cmd "")))))

(use-package consult-flycheck :ensure t :defer t :commands consult-flycheck)

(use-package consult-org-roam
  :ensure t
  :after org-roam
  :custom
  (consult-org-roam-grep-func #'consult-ripgrep)
  ;; Configure a custom narrow key for `consult-buffer'
  (consult-org-roam-buffer-narrow-key ?r)
  ;; Display org-roam buffers right after non-org-roam buffers
  ;; in consult-buffer (and not down at the bottom)
  (consult-org-roam-buffer-after-buffers nil)
  :config
  ;; Eventually suppress previewing for certain functions
  (consult-customize
   consult-org-roam-forward-links
   :preview-key (kbd "M-.")))

(use-package marginalia
  :ensure
  :init
  ;; Must be in the :init section of use-package such that the mode gets
  ;; enabled right away. Note that this forces loading the package.
  (marginalia-mode))

;; (use-package embark
;;   :ensure t
;;   :defer
;;   :bind (("C-." . embark-act)))

;; (use-package embark-consult
;;   :ensure t
;;   :after embark)

Kind Icon

(use-package kind-icon
  :ensure t
  :after corfu
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

pcomplete

(use-package pcmpl-args
  :ensure t
  :hook (eshell-first-time-mode . gopar/add-pcmpl-custom-commands)
  :init
  (defun gopar/add-pcmpl-custom-commands ()
                                     (dolist (command '("lsd" "pip3" "docker" "docker-compose" "ffmpeg"))
                                       (let ((alias-name (intern (concat "pcomplete/" command))))
                                         (eval `(defalias ',alias-name 'pcmpl-args-pcomplete-on-help))))

                                     (dolist (command '())
                                       (let ((alias-name (intern (concat "pcomplete/" command))))
                                         (eval `(defalias ',alias-name 'pcmpl-args-pcomplete-on-man))))

                                     (defalias 'pcomplete/pip 'pcomplete/pip3)))

(use-package pcmpl-homebrew :ensure t :after eshell)

Dumb Jump

A basic ‘go to’ functionality that works really well. So I don’t need LSP

(use-package dumb-jump
  :ensure t
  :defer
  :custom
  (dumb-jump-prefer-searcher 'ag)
  (dumb-jump-force-searcher 'ag)
  (dumb-jump-selector 'completing-read)
  (dumb-jump-default-project "~/work")
  :init
  (add-hook 'xref-backend-functions #'dumb-jump-xref-activate))

Xref

(use-package xref
  :ensure nil
  :defer t
  :init
  (defun gopar/xref-backend-html-template ()
    "Xref backend for jumping to HTML template definitions."
    (when (and (thing-at-point 'filename t) (string-suffix-p ".html" (thing-at-point 'filename t)))
      'gopar-html-template))

  (cl-defmethod xref-backend-identifier-at-point ((_backend (eql gopar-html-template)))
    (thing-at-point 'filename t))

  (cl-defmethod xref-backend-definitions ((_backend (eql gopar-html-template)) identifier)
    (let ((path (cl-find-if (lambda (x) (string-match-p identifier x))
                            (projectile-project-files (projectile-project-root)))))
      (when path
        (list (xref-make identifier (xref-make-file-location (format "%s%s" (projectile-project-root) path) 1 0))))))
  (add-hook 'xref-backend-functions #'gopar/xref-backend-html-template))

Eglot

(use-package eglot
  :ensure t
  :defer
  :init
  (setq gopar/ty-uvx-command
      '("uvx" "--from" "ty==0.0.13" "ty" "server"))
  ;; (setq gopar/pyright-uvx-command '("uvx" "--from" "pyright==1.1.407" "pyright-langserver" "--stdio"))
  :hook
  (eglot-managed-mode . (lambda ()
                          (setq xref-backend-functions '(gopar/xref-backend-html-template eglot-xref-backend dumb-jump-xref-activate t))))
  :bind (:map projectile-command-map
              ("R" . eglot-rename))
  :config
  ;; (add-to-list 'eglot-server-programs `(python-mode . ,gopar/pyright-uvx-command))
  (add-to-list 'eglot-server-programs `(python-mode . ,gopar/ty-uvx-command))
  (add-to-list 'eglot-stay-out-of 'flymake))

Elgot Header Line

(use-package eglot-header-line
  :ensure t
  :after eglot
  :vc (:url "https://github.com/soerlemans/eglot-header-line")
  :hook
  (eglot-managed-mode . eglot-header-line-mode))

To keep eldoc from displaying documentation at point without enabling any minor mode above: (add-to-list ‘eglot-ignored-server-capabilites :hoverProvider).

(use-package eldoc-box
  :ensure t
  :defer t
  :hook (eglot-managed-mode . eldoc-box-hover-at-point-mode)
  :custom
  (eldoc-box-clear-with-C-g t)
  )
(use-package eldoc-cmake
  :ensure t
  :defer t
  :hook (cmake-ts-mode . eldoc-cmake-enable))

Web Mode

(use-package web-mode
  :ensure t
  :defer
  :init
  (setq-default web-mode-code-indent-offset 2)
  (setopt web-mode-engines-alist '(("django"    . "\\.html\\'")))
  (setq web-mode-content-types-alist '(("jsx"  . "\\.js[x]?\\'")))
  (setopt web-mode-extra-snippets
      '(("django" . (("ifelif"  . "{% if | %}\n\n{% elif %}\n\n{% else %}\n\n{% endif %}")
                     ("ifelse"  . "{% if | %}\n\n{% else %}\n\n{% endif %}")
                     ("include" . "{% include \"|\" %}")
                     ("extends" . "{% extends \"|\" %}")))))

  :hook (web-mode . (lambda ()
                      (highlight-indentation-mode -1)
                      (electric-pair-local-mode -1)))
  :custom
  (web-mode-script-padding 0)
  (web-mode-enable-html-entities-fontification t)
  (web-mode-enable-element-content-fontification t)
  (web-mode-enable-current-element-highlight t)
  (web-mode-enable-current-column-highlight t)
  (web-mode-markup-indent-offset 2)
  (web-mode-css-indent-offset 2)
  (web-mode-sql-indent-offset 2)
  :mode (;; ("\\.vue\\'" . web-mode)
         ("\\.html\\'" . web-mode)
         ("\\.js[x]?\\'" . web-mode)
         ))

Emmet-mode

(use-package emmet-mode
  :ensure t
  :defer t
  :hook (web-mode . emmet-mode))

TypeScript

(use-package typescript-mode
  :ensure t
  :defer
  :bind (:map typescript-mode-map
              (";" . easy-camelcase))
  :custom
  (typescript-indent-level 2))

Markdown

(use-package markdown-mode
  :defer t
  :ensure t
  :bind (:map markdown-mode-map
              ("M-." . dictionary-lookup-definition)))

Dockerfile

(use-package dockerfile-mode
  :ensure t
  :defer)
(use-package docker
  :ensure t
  :defer
  :bind ("C-c d" . docker))

YAML

(use-package yaml-mode
  :ensure t
  :defer)

Rainbow mode

Color the string of whatever color code they are holding

(use-package rainbow-mode
  :defer
  :ensure t
  :hook (prog-mode . rainbow-mode))

Rainbow mode delimeters

(use-package rainbow-delimiters
  :ensure t
  :defer
  :hook prog-mode)

Alert

(use-package alert
  :ensure t
  :defer
  :custom
  (alert-default-style 'message)
  (alert-fade-time 5))

Which Key

(use-package which-key
  :ensure nil
  :hook (after-init . which-key-mode)
  :commands (which-key-enable-god-mode-support)
  :custom
  (which-key-idle-delay 2))

Helpful

(use-package helpful
  :ensure t
  ;; :hook ((helpful-mode . god-local-mode)
  ;;        (help-mode . god-local-mode))
  ;; :hook ((helpful-mode . view-mode)
  ;;        (help-mode . view-mode))
  :defer
  :bind (("C-h f" . helpful-callable)
         ("C-h v" . helpful-variable)
         ("C-h k" . helpful-key)))

Corral

(use-package corral
  :ensure t
  :defer
  :bind (("M-9" . corral-parentheses-backward)
         ("M-0" . corral-parentheses-forward)
         ("M-[" . corral-brackets-backward)
         ("M-]" . corral-brackets-forward)
         ("M-\"" . corral-single-quotes-backward)
         ("M-'" . corral-single-quotes-forward)))

Highlight Indentation

(use-package highlight-indentation
  :ensure t
  :defer
  :hook ((prog-mode . highlight-indentation-mode)
         (prog-mode . highlight-indentation-current-column-mode)))

Highlight TODO

(use-package hl-todo
  :ensure t
  :defer t
  :hook (prog-mode . hl-todo-mode))

Move Text

(use-package move-text
  :ensure t
  :defer
  :init (move-text-default-bindings))

Iedit

(use-package iedit
  :ensure t
  :defer
  :commands iedit-mode
  :bind (("C-c o" . iedit-mode))
  :custom
  (iedit-toggle-key-default (kbd "C-c o"))
  (iedit-auto-narrow t)
  (iedit-auto-save-occurrence-in-kill-ring nil))

Expand Region

(use-package expand-region
  :ensure t
  :defer
  :bind (("C-\\" . er/expand-region)))

So Long

(use-package so-long
  :ensure nil
  :hook (after-init . global-so-long-mode))

Avy

(use-package avy
  :ensure t
  :defer
  :bind (("M-g c" . avy-goto-char-2)
         ("M-g g" . avy-goto-line)
         ("M-g w" . avy-goto-word-1)))

All The Icons

(use-package all-the-icons
  :ensure t
  :defer
  :if (display-graphic-p))

(use-package all-the-icons-completion
  :ensure t
  :defer
  :hook (marginalia-mode . #'all-the-icons-completion-marginalia-setup)
  :init
  (all-the-icons-completion-mode))

Ibuffer

;; Ibuffer Icons sets it's own local buffer format and overrides the =ibuffer-formats= variable.
;; So in order for ibuffer-vc to work, I have to include it in the icons-buffer format -_-
(use-package all-the-icons-ibuffer
  :ensure t
  :defer
  :custom
  (all-the-icons-ibuffer-formats
   `((mark modified read-only locked vc-status-mini
           ;; Here you may adjust by replacing :right with :center or :left
           ;; According to taste, if you want the icon further from the name
           " " ,(if all-the-icons-ibuffer-icon
                    '(icon 2 2 :left :elide)
                  "")
           ,(if all-the-icons-ibuffer-icon
                (propertize " " 'display `(space :align-to 8))
              "")
           (name 25 25 :left :elide)
           " " (size-h 9 -1 :right)
           " " (mode+ 16 16 :left :elide)
           " " (vc-status 16 16 :left)
           " " vc-relative-file)
     (mark " " (name 16 -1) " " filename)))

  :hook (ibuffer-mode . all-the-icons-ibuffer-mode))

;; https://github.com/purcell/ibuffer-vc/blob/master/ibuffer-vc.el
(use-package ibuffer-vc
  :ensure t
  :defer
  :hook (ibuffer . (lambda ()
                     (ibuffer-vc-set-filter-groups-by-vc-root)
                     (unless (eq ibuffer-sorting-mode 'alphabetic)
                       (ibuffer-do-sort-by-vc-status)
                       ;; (ibuffer-do-sort-by-alphabetic)
                       )
                     )))

Webjump

(use-package webjump
  :defer
  :ensure nil
  :bind ("C-x /" . webjump)
  :config
  (setopt webjump-sites '(("ChatGPT" . [simple-query "https://chatgpt.com" "https://chatgpt.com/?prompt=" ""])
                          ("DuckDuckGo" . [simple-query "lite.duckduckgo.com" "lite.duckduckgo.com/lite?q=" ""])
                          ("Django Query" . [simple-query "lite.duckduckgo.com" "lite.duckduckgo.com/lite?q=django+" ""])
                          ("Django Rest Framework DRF" . "https://www.django-rest-framework.org/")
                          ("Django Classy Docs" . [simple-query "https://ccbv.co.uk/" "https://duckduckgo.com/?q=" "+site%3Ahttps%3A%2F%2Fccbv.co.uk&t=h_&ia=web" ])
                          ("DRF Classy Docs" . [simple-query "https://www.cdrf.co/" "https://duckduckgo.com/?t=h_&q=" "+site%3Ahttps%3A%2F%2Fwww.cdrf.co%2F"])))
  :config
  ;; Redefine read string so that we can have pre selected input
  (defun webjump-read-string (prompt)
    (let ((input (read-string (concat prompt ": ") (if (use-region-p) (buffer-substring-no-properties (region-beginning) (region-end)) nil))))
      (if (webjump-null-or-blank-string-p input) nil input))))

RFC Browsing

(use-package rfc-mode
  :defer
  :ensure t)

Electric Pair

(use-package elec-pair
  :ensure nil
  :defer
  :hook (after-init . electric-pair-mode))

Version Control

(use-package magit
  :ensure t
  :commands magit-get-current-branch
  :defer
  :bind ("C-x g" . magit)
  :hook (magit-mode . magit-wip-mode)
  :custom
  (magit-diff-refine-hunk 'all)
  (magit-process-finish-apply-ansi-colors t)
  (magit-format-file-function #'magit-format-file-all-the-icons)
  ;; Experimental speed boost (Mac only): https://www.reddit.com/r/emacs/comments/1qlnde7/comment/o1fq5lj/
  (magit-process-connection-type nil)
  :init
  (setopt magit-process-finish-apply-ansi-colors t)
  (defun magit/undo-last-commit (number-of-commits)
    "Undoes the latest commit or commits without loosing changes"
    (interactive "P")
    (let ((num (if (numberp number-of-commits)
                   number-of-commits
                 1)))
      (magit-reset-soft (format "HEAD^%d" num)))))

;; Part of magit
(use-package git-commit
  :ensure nil
  :after magit
  :hook (git-commit-setup . gopar/auto-insert-jira-ticket-in-commit-msg)
  :custom
  (git-commit-summary-max-length 80)
  :init
  (defun gopar/auto-insert-jira-ticket-in-commit-msg ()
    (let ((has-ticket-title (string-match "^[A-Z]+-[0-9]+" (magit-get-current-branch)))
          (words (s-split-words (magit-get-current-branch))))
      (if has-ticket-title
          (insert (format "[%s-%s] " (car words) (car (cdr words))))))))

(use-package magit-todos
  :ensure t
  :after magit
  :config (magit-todos-mode 1))

(use-package magit-pre-commit
  :ensure t
  :vc (:url "https://github.com/DamianB-BitFlipper/magit-pre-commit.el" :rev :newsest)
  :after magit)

(use-package git-gutter
  :ensure t
  :hook (after-init . global-git-gutter-mode))

Parens

(use-package paren
  :ensure nil
  :hook (after-init . show-paren-mode)
  :custom
  (show-paren-style 'mixed)
  (show-paren-context-when-offscreen t))

Battery

(use-package battery
  :ensure nil
  :hook (after-init . display-battery-mode))

Yasnippet

;; After adding or updating a snippet run:
;; =M-x yas-recompile-all=
;; =M-x yas-reload-all=
(use-package yasnippet
  :ensure t
  :defer
  :hook ((prog-mode . yas-minor-mode)
         (org-mode . yas-minor-mode)
         (fundamental-mode . yas-minor-mode)
         (text-mode . yas-minor-mode)
         (eshell-mode . yas-minor-mode)
         ;; (after-init . yas-reload-all)
         ))

Actual Snippets

(use-package yasnippet-snippets
  :ensure t
  :defer)

To use as a super capf with a few others

(use-package yasnippet-capf
  :ensure t
  :defer t
  :vc (:url "https://github.com/elken/yasnippet-capf" :rev :newsest)
  )

Consult Yasnippet

(use-package consult-yasnippet
  :ensure t
  :after yasnippet
  :bind (:map yas-minor-mode-map
              ("C-c C-SPC" . consult-yasnippet)))
(use-package dashboard
  :ensure t
  :custom
  (dashboard-startup-banner 'logo)
  (dashboard-center-content t)
  (dashboard-show-shortcuts nil)
  (dashboard-set-heading-icons t)
  (dashboard-icon-type 'all-the-icons)
  (dashboard-set-file-icons t)
  (dashboard-projects-backend 'projectile)
  (dashboard-items '(
                     (vocabulary)
                     (recents . 5)
                     (bookmarks . 5)
                     ;; (monthly-balance)
                     ))
  (dashboard-item-generators '(;; (monthly-balance . gopar/dashboard-ledger-monthly-balances)
                              (vocabulary . gopar/dashboard-insert-vocabulary)
                              (recents . dashboard-insert-recents)
                              (bookmarks . dashboard-insert-bookmarks)
                              ))
  :init
  (defun gopar/dashboard-insert-vocabulary (list-size)
    (dashboard-insert-heading "Word of the Day:"
                              nil
                              (all-the-icons-faicon "newspaper-o"
                                                    :height 1.2
                                                    :v-adjust 0.0
                                                    :face 'dashboard-heading))
    (insert "\n")
    (let ((random-line nil)
          (lines nil))
      (with-temp-buffer
        (insert-file-contents (concat user-emacs-directory "words"))
        (goto-char (point-min))
        (setq lines (split-string (buffer-string) "\n" t))
        (setq random-line (nth (random (length lines)) lines))
        (setq random-line (string-join (split-string random-line) " ")))
      (insert "    " random-line)))

  (defun gopar/dashboard-ledger-monthly-balances (list-size)
    (interactive)
    (dashboard-insert-heading "Monthly Balance:"
                              nil
                              (all-the-icons-faicon "money"
                                                    :height 1.2
                                                    :v-adjust 0.0
                                                    :face 'dashboard-heading))
    (insert "\n")
    (let* ((categories '("Expenses:Food:Restaurants"
                         "Expenses:Food:Groceries"
                         "Expenses:Misc"))
           (current-month (format-time-string "%Y/%m"))
           (journal-file (expand-file-name "~/personal/finances/main.dat"))
           (cmd (format "ledger bal --flat --monthly --period %s %s -f %s"
                        current-month
                        (mapconcat 'identity categories " ")
                        journal-file)))

      (insert (shell-command-to-string cmd))))
  :config
  (dashboard-setup-startup-hook))

Display Fill Column

Collides with compact-docstrings so turning off for programming modes

(use-package display-fill-column-indicator
  :ensure nil
  :hook (;; (python-mode . display-fill-column-indicator-mode)
         (org-mode . display-fill-column-indicator-mode))
  )

Dired

(use-package dired
  :ensure nil
  :bind ("C-x C-d" . dired)
  :defer
  :hook ((dired-mode . dired-hide-details-mode)
         (dired-mode . hl-line-mode))
  :custom
  (dired-do-revert-buffer t)
  (dired-auto-revert-buffer t)
  (delete-by-moving-to-trash t)
  (dired-mouse-drag-files t)
  (dired-dwim-target t)
  ;; (dired-guess-shell-alist-user)
  (dired-listing-switches "-AlhoF --group-directories-first"))

(use-package all-the-icons-dired
  :ensure t
  :defer
  :hook (dired-mode . all-the-icons-dired-mode)
  :custom
  (all-the-icons-dired-monochrome nil))

(use-package files
  :ensure nil
  :defer t
  :custom
  (insert-directory-program "gls") ; Will not work if system does not have GNU gls installed
  ;; Don't have backup
  (backup-inhibited t)
  ;; Don't save anything.
  (auto-save-default nil)
  ;; If file doesn't end with a newline on save, automatically add one.
  (require-final-newline t)
  ;; Switch to view-mode when in read-only mode
  ;; (view-read-only t)
  :config
  (add-to-list 'auto-mode-alist '("Pipfile" . conf-toml-mode)))

Dired Subtree

(use-package dired-subtree
  :ensure t
  :after dired
  :bind (:map dired-mode-map
              ("<tab>" . dired-subtree-toggle)
              ("<C-tab>" . dired-subtree-cycle)
              ("<backtab>" . dired-subtree-remove) ;; Shift + Tab
              ))

Replace/Occur

(use-package replace
  :ensure nil
  :defer
  :custom
  (list-matching-lines-default-context-lines 0)
  (list-matching-lines-face nil)
  :bind (("C-c C-o" . gopar/occur-definitions)
         :map occur-mode-map
         ("n" . occur-next)
         ("p" . occur-prev))
  :init
  (add-to-list 'display-buffer-alist
               '("\\*Occur"
                 display-buffer-in-side-window
                 (side . left)
                 (slot . 1)
                 (dedicated . t)
                 (window-parameters . ((no-delete-other-windows . t)))
                 (window-width . 30)))

  (defun gopar/occur-definitions ()
    "Show all the function/method/class definitions for the current language."
    (interactive)
    (cond
     ((eq major-mode 'emacs-lisp-mode)
      (occur "\(defun"))
     ((or (eq major-mode 'python-mode) (eq major-mode 'python-ts-mode))
      (occur "^\s*\\(\\(async\s\\|\\)def\\|class\\)\s"))
     ;; If no matching, then just do regular occur
     (t (call-interactively 'occur)))))

Occur-x

Only really used to remove lines to the right of margin.

Can honestly just rip out relevant part to remove lines vs installing this library

(use-package occur-x
  :ensure t
  :hook (occur-mode . turn-on-occur-x-mode)
  :custom
  (occur-linenumbers-in-margin 'none)
  :config
  (defun occur-x--linenums-to-margin()
    "Custom function that overwrites library default to completely remove lines"
    (save-excursion
      (when (not (equal occur-linenumbers-in-margin 'none))
        (occur-x--set-margin))
      (goto-char (point-min))
      (forward-line 1)
      (let ((inhibit-read-only t)
            (context (cadr occur-revert-arguments))
            width side)
        (if (equal occur-linenumbers-in-margin 'right-margin)
            (setq width right-margin-width
                  side 'right-margin)
          (setq width left-margin-width
                side 'left-margin))
        (while (not (eobp))
          (if (looking-at "^\s*\\([0-9]+\\):")
              (if (equal occur-linenumbers-in-margin 'none)
                  (delete-region (point) (match-end 0))
                (let ((n (propertize
                          (format (format "%%%ds" width) (match-string 1))
                          'face 'occur-margin-face))
                      (o (make-overlay (point) (point))))
                  (delete-region (point) (match-end 0))
                  (overlay-put o 'before-string
                               (propertize " " 'display
                                           `((margin ,side) ,n)))))
            (if (and context (looking-at "^\s+:"))
                (delete-region (point) (match-end 0))))
          (forward-line 1)))))
  )

Ansi Color

(use-package ansi-color
  :ensure nil
  :defer
  :hook (compilation-filter . ansi-color-compilation-filter)
  :init
  (defvar gopar-ansi-escape-re
    (rx (or ?\233 (and ?\e ?\[))
        (zero-or-more (char (?0 . ?\?)))
        (zero-or-more (char ?\s ?- ?\/))
        (char (?@ . ?~))))

  (defun gopar/nuke-ansi-escapes (beg end)
    (save-excursion
      (goto-char beg)
      (while (re-search-forward gopar-ansi-escape-re end t)
        (replace-match ""))))

  (defun gopar/compilation-nuke-ansi-escapes ()
    (toggle-read-only)
    (gopar/nuke-ansi-escapes (point-min) (point-max))
    (toggle-read-only))

  ;; https://stackoverflow.com/questions/3072648/cucumbers-ansi-colors-messing-up-emacs-compilation-buffer
  (defun gopar/colorize-compilation-buffer ()
    "Colorize the output from compile buffer"
    (read-only-mode -1)
    (ansi-color-apply-on-region (point-min) (point-max))
    (read-only-mode 1)))

JS

(use-package js
  :defer
  :bind (:map js-mode-map
              (";" . easy-camelcase)

              :map js-jsx-mode-map
              (";" . easy-camelcase))
  :custom
  (js-indent-level 2)
  (js-jsx-indent-level 2))

Vue

(use-package vue-mode
  :ensure t
  :defer
  :hook ((vue-mode . flycheck-mode)
         (vue-mode . (lambda () (jinx-mode -1))))
  :init
  (add-hook 'mmm-mode-hook
            (lambda ()
              (set-face-background 'mmm-default-submode-face nil)))
  :config
  (setopt mmm-submode-decoration-level 0))

Pulse

(use-package pulse
  :ensure nil
  :defer
  :init
  (defun pulse-line (&rest _)
    "Pulse the current line."
    (pulse-momentary-highlight-one-line (point)))

  (dolist (command '(scroll-up-command
                     scroll-down-command
                     windmove-left
                     windmove-right
                     windmove-up
                     windmove-down
                     move-to-window-line-top-bottom
                     recenter-top-bottom
                     other-window))
    (advice-add command :after #'pulse-line)))

Mouse Scroll

For my mouse that also has left - right mouse scroll

(use-package mwheel
  :ensure nil
  :defer
  :custom
  (mouse-wheel-tilt-scroll t)
  (mouse-wheel-scroll-amount-horizontal 2)
  (mouse-wheel-flip-direction t))

Whitespace

(use-package whitespace
  :ensure nil
  :defer
  :hook (before-save . whitespace-cleanup)
  :custom
  (whitespace-line-column nil))

Auto revert

(use-package autorevert
  :ensure nil
  :defer
  :hook (emacs-startup . global-auto-revert-mode))

Simple

Built in package that holds a few goodies

(use-package simple
  :ensure nil
  :defer
  :hook ((makefile-mode . indent-tabs-mode)
         (fundamental-mode . delete-selection-mode)
         (fundamental-mode . auto-fill-mode)
         (org-mode . auto-fill-mode))
  :custom
  (save-interprogram-paste-before-kill nil)
  (kill-do-not-save-duplicates t)
  (kill-read-only-ok t)
  (kill-transform-function 'substring-no-properties)
  (yank-excluded-properties t)

  ;; Shell related things
  (shell-command-prompt-show-cwd t)

  :init
  (defun gopar/pulse-current-region (&rest _)
  "Pulse the current implicit or active region."
  (if mark-active
      (pulse-momentary-highlight-region (region-beginning) (region-end))
    (pulse-momentary-highlight-region (mark) (point))))

  (advice-add #'kill-ring-save :before #'gopar/pulse-current-region))

Neotree

Here since treemacs keeps on breaking, and this is backup. Works pretty well

(use-package neotree
  :ensure t
  :bind ("<f5>" . neotree-toggle)
  :hook (emacs-startup . neotree)
  :custom
  (neo-theme 'icons)
  (neo-smart-open t)
  (neo-autorefresh t)
  (neo-window-width 35)
  (neo-toggle-window-keep-p t)
  ;; takes too long to update on first try
  ;; (neo-vc-integration '(face char))
  (neo-show-hidden-files nil)
  (neo-display-action '(gopar/neo-display-fn))
  :init
  (defun gopar/neo-display-fn (buffer _alist)
    (let ((window-pos (if (eq neo-window-position 'left) 'left 'right)))
      (display-buffer-in-side-window buffer `((side . ,window-pos)
                                              (inhibit-same-window . t)
                                              (dedicated . t)
                                              (window-parameters
                                               (no-delete-other-windows . t)
                                               (no-other-window . t)))))))

Dizze

Unfortunately need this: davidmiller/dizzee#5

Sooo I manually copied the PR fix into the init section. Sigh.

(use-package dizzee
  :ensure t
  :defer)

String Inflection

(use-package string-inflection
  :ensure t
  :defer
  :commands string-inflection-insert
  :bind (("C-;" . gopar/string-inflection-cycle-auto))
  :init
  (defun string-inflection-web-mode-function (str)
    "foo_bar => fooBar => Foo_Bar => FOO_BAR => fooBar"
    (cond
     ((string-inflection-underscore-p str)
      (string-inflection-camelcase-function str))
     ((string-inflection-camelcase-p str)
      (string-inflection-pascal-case-function str))
     ((string-inflection-pascal-case-p str)
      (string-inflection-upcase-function str))
     (t
      (string-inflection-camelcase-function str))))

  (defun string-inflection-web-mode-style-cycle ()
    "foo_bar => FOO_BAR => FooBar => foo_bar"
    (interactive)
    (string-inflection--single-or-region #'string-inflection-web-mode-function))

  (defun gopar/string-inflection-cycle-auto ()
    "Switching by major mode."
    (interactive)
    (cond
     ((eq major-mode 'emacs-lisp-mode)
      (string-inflection-all-cycle))

     ((or (eq major-mode 'python-mode) (eq major-mode 'python-ts-mode))
      (string-inflection-python-style-cycle))

     ((or (eq major-mode 'js-mode)
          (eq major-mode 'vue-mode)
          (eq major-mode 'java-mode)
          (eq major-mode 'typescript-mode))
      (string-inflection-java-style-cycle))

     ((eq major-mode 'nxml-mode)
      (string-inflection-java-style-cycle))

     ((eq major-mode 'hy-mode)
      (string-inflection-kebab-case))

     ((eq major-mode 'web-mode)
      (string-inflection-web-mode-style-cycle))

     (t
      (string-inflection-ruby-style-cycle)))))

String Edit

Only available in 29 or higher

(use-package string-edit
  :ensure nil
  :defer
  :init
  (defun gopar/replace-str-at-point (new-str)
    (let ((bounds (bounds-of-thing-at-point 'string)))
      (when bounds
        (delete-region (car bounds) (cdr bounds))
        (insert new-str))))

  (defun gopar/edit-string-at-point ()
    (interactive)
    (let ((string (thing-at-point 'string t)))
      (string-edit "String at point:" string 'gopar/replace-str-at-point :abort-callback (lambda ()
                     (exit-recursive-edit)
                     (message "Aborted edit"))))))

Compact Docstring

(use-package compact-docstrings
  :ensure t
  :defer
  :hook (prog-mode . compact-docstrings-mode)
  :custom
  (compact-docstrings-only-doc-blocks nil))

Transient

(use-package transient
  :ensure t
  :defer
  :bind ("C-M-o" . windows-transient-window)
  :config
  (transient-define-prefix windows-transient-window ()
   "Display a transient buffer showing useful window manipulation bindings."
    [["Resize"
     ("}" "h+" enlarge-window-horizontally :transient t)
     ("{" "h-" shrink-window-horizontally :transient t)
     ("^" "v+" enlarge-window :transient t)
     ("V" "v-" shrink-window :transient t)]
     ["Split"
    ("v" "vertical" (lambda ()
       (interactive)
       (split-window-right)
       (windmove-right)) :transient t)
    ("x" "horizontal" (lambda ()
       (interactive)
       (split-window-below)
       (windmove-down)) :transient t)]
    ["Misc"
     ("b" "Balance Windows" balance-windows :transient t)
     ("z" "undo" (lambda ()
                  (interactive)
                  (winner-undo)
                  (setq this-command 'winner-undo)) :transient t)
    ("Z" "redo" winner-redo :transient t)]]
    [["Move"
    ("h" "" windmove-left :transient t)
    ("j" "" windmove-down :transient t)
    ("l" "" windmove-right :transient t)
    ("k" "" windmove-up :transient t)]
    ["Swap"
     ("s" "Swap" ace-swap-window)]
    ["Delete"
    ("dh" "" windmove-delete-left :transient t)
    ("dj" "" windmove-delete-down :transient t)
    ("dl" "" windmove-delete-right :transient t)
    ("dk" "" windmove-delete-up :transient t)
    ("D" "This" delete-window :transient t)]
    ]))

Ace Window

(use-package ace-window
  :ensure t
  :defer t
  :bind ("C-x o" . ace-window)
  :hook ((after-init . ace-window-display-mode)
         (after-init . ace-window-posframe-mode))
  :custom
  (aw-keys '(?a ?s ?d ?f ?g ?h ?k ?l))
  (aw-dispatch-alist '((?x aw-delete-window "Delete Window")
                         (?m aw-swap-window "Swap Windows")
                         (?M aw-move-window "Move Window")
                         (?c aw-copy-window "Copy Window")
                         (?j aw-switch-buffer-in-window "Select Buffer")
                         (?n aw-flip-window)
                         (?u aw-switch-buffer-other-window "Switch Buffer Other Window")
                         (?e aw-execute-command-other-window "Execute Command Other Window")
                         (?F aw-split-window-fair "Split Fair Window")
                         (?v aw-split-window-vert "Split Vert Window")
                         (?b aw-split-window-horz "Split Horz Window")
                         (?B balance-windows)
                         (?o delete-other-windows "Delete Other Windows")
                         (?T aw-transpose-frame "Transpose Frame")
                         ;; ?i ?r ?t are used by hyperbole.el
                         (?? aw-show-dispatch-help)))
  :config
  (add-to-list 'aw-ignored-buffers " *NeoTree*"))

Vterm

Set up directory tracking: https://github.com/akermu/emacs-libvterm?tab=readme-ov-file#vterm-buffer-name-string

(use-package vterm
  :ensure t
  :after projectile
  :bind ((:map vterm-mode-map ("C-q" . vterm-send-next-key))
         ;; :map projectile-command-map
         ;; ("x v" . gopar/projectile-vterm)
         )
  :custom
  (vterm-buffer-name-string nil ;; "*vterm %s*"
                            )
  (vterm-max-scrollback 100000)
  :config
  (add-to-list 'vterm-keymap-exceptions "C--")
  (add-to-list 'vterm-keymap-exceptions "M--")
  (add-to-list 'vterm-keymap-exceptions "C-M--")
  ;; Doesn't seem to work??
  ;; (add-to-list 'vterm-keymap-exceptions "C-M-o")
  :init
  (defvar gopar/vterm-main-session "main"
    "Fallback tmux session name.")

  (defun gopar/project-tmux-session-name ()
    "Return tmux session name matching zsh auto-tmux logic.
Uses presence of git worktrees directory as the primary signal."
    (when (projectile-project-p)
      (let* ((root (directory-file-name (projectile-project-root)))
             (git-common-dir (string-trim
                              (shell-command-to-string
                               "git rev-parse --git-common-dir 2>/dev/null")))

             ;; does this repo use worktrees?
             (has-worktrees
              (file-directory-p
               (expand-file-name "worktrees" git-common-dir)))

             ;; repo naming logic
             (repo
              (if has-worktrees
                  ;; linked worktree → repo is parent of worktree root
                  (file-name-nondirectory
                   (directory-file-name
                    (file-name-directory root)))
                ;; normal repo OR main worktree
                (file-name-nondirectory root)))

             ;; branch name
             (branch (string-trim
                      (shell-command-to-string
                       "git branch --show-current 2>/dev/null"))))

        ;; detached HEAD handling
        (when (string-empty-p branch)
          (setq branch "detached"))

        ;; sanitize for tmux
        (replace-regexp-in-string
         "[^[:alnum:]_-]" "_"
         (format "%s__%s" repo branch)))))


  (defun gopar/get-or-create-vterm (&optional new)
    "Return the main vterm buffer.
If NEW is non-nil, always create a new vterm."
    (if new
        (vterm "*vterm*")
      (or (get-buffer "*vterm*")
          (vterm "*vterm*"))))

  (defun gopar/vterm-switch-tmux-session (buf session directory)
    "Tell tmux inside BUF to switch to SESSION."
    (with-current-buffer buf
      (when (derived-mode-p 'vterm-mode)
        (let* ((cmd
                (format
                 "tmux has-session -t %s 2>/dev/null || \
tmux new-session -ds %s -c %s -n shell \\; \
new-window -t %s -c %s -n cecli; \
tmux switch-client -t %s\n"
                 session session directory
                 session directory
                 session)))
          (vterm-send-string cmd)
          (vterm-send-return)))))

  (defun gopar/projectile-vterm (&optional arg)
    "Open a single shared vterm and switch tmux session.
With prefix ARG, create a new vterm."
    (interactive "P")
    (let* ((session (or (gopar/project-tmux-session-name)
                        gopar/vterm-main-session))
           (buf (gopar/get-or-create-vterm arg)))
      (pop-to-buffer buf)
      (unless arg
        (gopar/vterm-switch-tmux-session buf session (projectile-project-root)))))

  ;; (defun gopar/kill-primary-vterm ()
;;     "Kill the primary *vterm* buffer if it exists."
;;     (let ((buf (get-buffer "*vterm*"))
;;           (kill-buffer-query-functions nil))
;;       (when (and buf (buffer-live-p buf))
;;         (kill-buffer buf))))

;;   (defun (gopar/vterm) (&optional arg)
;;     "Create a new vterm.
;; Without prefix: replace *vterm*.
;; With prefix: create an additional vterm."
;;     (interactive "P")
;;     (if arg
;;         ;; Extra vterm (side-by-side, debug, etc)
;;         (vterm)
;;       ;; Primary illusion
;;       (gopar/kill-primary-vterm)
;;       (let ((buf (vterm)))
;;         (with-current-buffer buf
;;           (rename-buffer "*vterm*" t))
;;         (pop-to-buffer buf))))
  )

Exec From Shell

(use-package exec-path-from-shell
  :ensure t
  :init
  (setq exec-path-from-shell-variables '("PATH" "MANPATH" "SSH_AUTH_SOCK" "NVM_DIR" "HOMEBREW_NO_AUTO_UPDATE" "OPENAI_API_KEY" "ANTHROPIC_API_KEY" "DEEPSEEK_API_KEY"))
  (exec-path-from-shell-initialize))
(use-package devdocs
  :ensure t
  :defer
  :bind ("C-c M-d" . gopar/devdocs-lookup)
  :init
  (add-to-list 'display-buffer-alist
               '("\\*devdocs\\*"
                 display-buffer-in-side-window
                 (side . right)
                 (slot . 3)
                 (window-parameters . ((no-delete-other-windows . t)))
                 (dedicated . t)))

  (defun gopar/devdocs-lookup (&optional ask-docs)
    "Light wrapper around `devdocs-lookup` which pre-populates the function input with thing at point"
    (interactive "P")
    (let ((query (thing-at-point 'symbol t)))
      (devdocs-lookup ask-docs query)))


  :hook (((python-mode python-ts-mode) . (lambda () (setq-local devdocs-current-docs
                                      '("django~4.2" "django_rest_framework" "python~3.11" "postgresql~12"))))
         (web-mode . (lambda () (setq-local devdocs-current-docs '("vue~3"
                                                                   "vue_router~4"
                                                                   "javascript"
                                                                   "typescript"
                                                                   "vitest"
                                                                   "moment"
                                                                   "tailwindcss"
                                                                   "html"
                                                                   "css"))))
         ((typescript-mode typescript-ts-mode) . (lambda () (setq-local devdocs-current-docs '("vue~3"
                                                                          "vue_router~4"
                                                                          "javascript"
                                                                          "typescript"
                                                                          "vitest"
                                                                          "moment"))))
         ((c++-ts-mode c++-mode) . (lambda () (setq-local devdocs-current-docs '("cpp"))))
         (devdocs-mode . (lambda () (toggle-truncate-lines) ;; (toggle-word-wrap)
                           ))
         ))

Link Hint

(use-package link-hint
  :ensure t
  :defer)

Flycheck

(use-package flycheck
  :ensure
  :defer
  :hook (((python-mode python-ts-mode) . flycheck-mode))
  :bind (:map flycheck-mode-map
              ("M-g n" . flycheck-next-error)
              ("M-g p" . flycheck-previous-error))
  :init
  ;; (setq fit-window-to-buffer-horizontally t)
  ;; (setq window-resize-pixelwise t)
  (setopt window-sides-vertical t)
  (add-to-list 'display-buffer-alist
               '("\\*Flycheck errors\\*"
                 display-buffer-in-side-window
                 (side . bottom)
                 (slot . -1)
                 (no-delete-other-windows . t)
                 (window-height . .15)))

  ;; Experimental
  (defun gopar/flycheck-update-error-list ()
    "Display errors using flycheck-list-errors if there are any errors.
If there are no errors, kill the '*Flycheck errors*' buffer."
    (if flycheck-current-errors
        (flycheck-list-errors)
      (let ((errors-buffer (get-buffer "*Flycheck errors*")))
        (when errors-buffer
          (kill-buffer errors-buffer)))))

  :custom
  (flycheck-flake8rc '(".flake8" "setup.cfg" "tox.ini" "pyproject.toml")))

AI Stuff

Aidermacs

(use-package aidermacs
  :ensure t
  :bind (("M-g a" . aidermacs-transient-menu)
         :map aidermacs-vterm-mode-map
         ("M-RET" . aidermacs-vterm-send-return)
         )
  :init
  (setopt aidermacs-vterm-use-theme-colors nil)
  :custom
  ; This doens't work along with M-RET. Need to open ticket
  (aidermacs-vterm-multiline-newline-key "RET")
  (aidermacs-vterm-use-theme-colors nil)
  (aidermacs-backend 'vterm)
  (aidermacs-exit-kills-buffer t)
  (aidermacs-show-diff-after-change nil)
  (aidermacs-program "~/projs/cecli/.venv/bin/cecli")
  (aidermacs-config-file "~/.config/cecli/cecli.conf.yml")
  )

Better UI for Modeline. Need to install fonts first by doing this

M-x all-the-icons-install-fonts
(use-package doom-modeline
  :ensure t
  :hook (emacs-startup . doom-modeline-mode)
  :config (column-number-mode 1)
  :custom
  (doom-modeline-height 30)
  (doom-modeline-window-width-limit nil)
  (doom-modeline-buffer-file-name-style 'truncate-with-project)
  (doom-modeline-minor-modes nil)
  (doom-modeline-enable-word-count nil)
  (doom-modeline-buffer-encoding nil)
  (doom-modeline-buffer-modification-icon t)
  (doom-modeline-env-python-executable "python")
  ;; needs display-time-mode to be one
  (doom-modeline-time t)
  (doom-modeline-vcs-max-length 50)
  )

Doom Themes

Use Doom Themes since they have built in support for Solaire Mode

(use-package doom-themes
  :ensure t
  :hook (after-init . (lambda () (load-theme 'modus-vivendi-tritanopia)))
  :custom
  (doom-gruvbox-brighter-comments t)
  :config
  ;; Global settings (defaults)
  (setopt doom-themes-enable-bold t    ; if nil, bold is universally disabled
          doom-themes-enable-italic t) ; if nil, italics is universally disabled

  ;; 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 tao-theme
  :ensure t
  :custom
  (tao-theme-use-sepia nil)
  (tao-theme-use-height nil) ;; doens't look that great in org mode
  :init
  (add-hook 'enable-theme-functions
            (lambda (theme)
              (when (eq theme 'tao-yin)
                (custom-set-faces
                 '(flycheck-error ((t (:underline (:color "#ff5f5f" :style wave)))))
                 '(flycheck-warning ((t (:underline (:color "#ffaf00" :style wave)))))
                 '(flycheck-info ((t (:underline (:color "#5fff87" :style wave)))))))))
  )
(use-package fill-function-arguments
  :ensure t
  :defer
  :bind (:map prog-mode-map
              ("M-q" . fill-function-arguments-dwim)))
(use-package ssh-config-mode
  :ensure t
  :defer)
(use-package hide-mode-line
  :ensure t
  :defer
  :hook (;; (eshell-mode . hide-mode-line-mode)
         ;; (vterm-mode . hide-mode-line-mode)
         ;; (occur-mode . hide-mode-line-mode)
         (neotree-mode . hide-mode-line-mode)))

Sqlite

Built in sqlite viewer in emacs Additional goodies can be found here: https://xenodium.com/further-sqlite-mode-extensions/

(use-package sqlite-mode
  :ensure nil
  :defer
  :bind (:map sqlite-mode-map
              ("n" . next-line)
              ("p" . previous-line)))

SQL Mode

Shells for db’s

(use-package sql
  :ensure nil
  :defer
  :custom
  (sql-sqlite-options '("-header" "-box")))

Keycast

Display keystors in mode line. Useful for when making videos

(use-package keycast
  :ensure t
  :defer
  :custom
  (keycast-mode-line-format "%k%c%R ")
  (keycast-substitute-alist
   '((keycast-log-erase-buffer nil nil)
     (transient-update         nil nil)
     (self-insert-command "." "Typing…")
     (org-self-insert-command "." "Typing…")
     (mwheel-scroll nil nil)
     (mouse-movement-p nil nil)
     (mouse-event-p nil nil))))

Dot Env Mode

(use-package dotenv-mode
  :ensure t
  :defer
  :hook (dotenv-mode . (lambda () (focus-mode -1)))
  :mode ("\\.env\\..*\\'" . dotenv-mode))

Dot Env Hide

(use-package dotenv-hide
  :load-path "lisp/modes/dotenv-hide"
  :hook (dotenv-mode . dotenv-hide-mode))
(use-package focus
  :ensure t
  :defer
  :hook ((prog-mode . focus-mode)
         (docker-ts-mode . (lambda () (focus-mode -1)))
         (typescript-ts-mode . (lambda () (focus-mode -1)))
         (sh-mode . (lambda () (focus-mode -1)))
         (web-mode . (lambda () (focus-mode -1)))))

Fretboard

Guitar stuff

(use-package fretboard
  :ensure t
  :defer t
  :commands fretboard
  :custom
  (fretboard-fret-count 20))

Popper

(use-package popper
  :ensure t
  :defer
  :bind (("C--"   . popper-toggle)
         ("M--"   . popper-cycle)
         ("C-M--" . popper-toggle-type))
  :hook ((emacs-startup . popper-mode)
         (emacs-startup . popper-echo-mode))
  :custom
  (popper-display-control t)
  (popper-group-function 'popper-group-by-directory)
  (popper-reference-buffers
   '("\\*Messages\\*"
     "Output\\*$"
     "\\*Async Shell Command\\*"
     "\\*Warnings\\*"
     "\\*tmux-help\\*"
     helpful-mode
     help-mode
     eshell-mode
     ;; vterm-mode
     world-clock-mode
     eww-buffers-mode
     compilation-mode)))

Shift Number

Not sure why I got rid of this. Its handy

(use-package shift-number
  :ensure t
  :defer
  :bind (("M-+" . shift-number-up)
         ("M-_" . shift-number-down)))

Code Cognitive Complexity

(use-package cognitive-complexity
  :ensure t
  :vc (:rev :newest :url "https://github.com/emacs-vs/cognitive-complexity")
  :hook (python-ts-mode . cognitive-complexity-mode))
(use-package envrc
  :ensure t
  :defer
  :hook (after-init . envrc-global-mode))

GNU Ledger

(use-package ledger-mode
  :ensure t
  :defer t)

Flycheck Ledger

(use-package flycheck-ledger
  :ensure t
  :defer t)

1st Party Modes

Stuff I’ve made for myself that are modes

Pair Programming

(defvar gopar-pair-programming nil)
(defun gopar/pair-programming ()
  "Poor mans minor mode for setting up things that i like to make pair programming easier."
  (interactive)
  (if gopar-pair-programming
      (progn
        ;; Don't use global line numbers mode since it will turn on in other modes that arent programming
        (dolist (buffer (buffer-list))
          (with-current-buffer buffer
            (when (derived-mode-p 'prog-mode)
              (display-line-numbers-mode -1))))
        (remove-hook 'prog-mode-hook 'display-line-numbers-mode)
        (setq gopar-pair-programming nil))

    (progn
      ;; display line numbers
      (dolist (buffer (buffer-list))
        (with-current-buffer buffer
          (when (derived-mode-p 'prog-mode)
            (display-line-numbers-mode 1))))
      (add-hook 'prog-mode-hook 'display-line-numbers-mode)

      (neotree)
      (setq gopar-pair-programming t))))

Boolcase

(use-package boolcase
  :load-path "lisp/modes/boolcase"
  :hook ((python-mode python-ts-mode) . boolcase-mode))

YouTube

Functions that are handy for setting up recording in youtube

(defvar gopar/orginal-font-height nil)
(defvar gopar/youtube-font-height 220)
(defvar gopar/original-neo-width 35)

(defun gopar/youtube-setup ()
  (when (null gopar/orginal-font-height)
    (setq gopar/orginal-font-height (face-attribute 'default :height)))

  (set-face-attribute 'default nil :height gopar/youtube-font-height)
  (set-frame-size (selected-frame) 143 40)

  (delete-other-windows)
  (display-time-mode -1)
  (type-break-mode -1)
  (keycast-header-line-mode)
  (setq neo-window-width 20)
  (let ((dashboard-items '((vocabulary) ;; (bookmarks . 5)
                           ))
        ;; (dashboard-banner-logo-title "✨ Memberships are available. Thank you for the support! ✨")
        )
    (dashboard-open)))

(defun gopar/youtube-setup-v2 ()
  (progn
    (add-to-list 'default-frame-alist `(font . "Hack 19"))
    (set-face-attribute 'default nil :font "Hack 19"))

  (set-frame-size (selected-frame) 172 47)

  (delete-other-windows)
  (keycast-header-line-mode)
  (setq neo-window-width 20)
  (let ((dashboard-items '((vocabulary))))
    (dashboard-open)))

(defun gopar/youtube ()
  (interactive)
  (consult-theme 'modus-vivendi-tritanopia)
  (gopar/youtube-setup-v2))

tmux

Still learning tmux commands so this helps

(defun tmux-help ()
  "Show a temporary buffer with default tmux keybindings and common inspection commands."
  (interactive)
  (let ((buf (get-buffer-create "*tmux-help*")))
    (with-current-buffer buf
      (read-only-mode -1)
      (erase-buffer)

      (insert
       "TMUX QUICK HELP (default keybindings)\n"
       "==================================\n\n"

       "Prefix key: C-b\n\n"

       "SESSIONS\n"
       "--------\n"
       "C-b d        Detach from session\n"
       "C-b s        Choose session (tree view)\n"
       "C-b $        Rename current session\n"
       "C-b (        Switch to previous session\n"
       "C-b )        Switch to next session\n\n"

       "WINDOWS\n"
       "-------\n"
       "C-b c        Create new window\n"
       "C-b w        Choose window (tree view)\n"
       "C-b n        Next window\n"
       "C-b p        Previous window\n"
       "C-b ,        Rename window\n"
       "C-b &        Kill window\n"
       "C-b 0–9      Switch to window number\n\n"

       "PANES\n"
       "-----\n"
       "C-b %        Split pane vertically\n"
       "C-b \"        Split pane horizontally\n"
       "C-b o        Next pane\n"
       "C-b ;        Last active pane\n"
       "C-b x        Kill pane\n"
       "C-b z        Zoom pane (toggle)\n"
       "C-b q        Show pane numbers\n"
       "C-b {        Move pane left\n"
       "C-b }        Move pane right\n\n"

       "RESIZE PANES\n"
       "------------\n"
       "C-b + Arrow  Resize pane (hold prefix)\n\n"

       "COPY / SCROLL MODE\n"
       "------------------\n"
       "C-b [        Enter copy mode\n"
       "Space        Start selection (vi/emacs mode dependent)\n"
       "Enter        Copy selection\n"
       "C-b ]        Paste buffer\n\n"

       "SEARCH (copy mode)\n"
       "------------------\n"
       "/            Search forward\n"
       "?            Search backward\n"
       "n / N        Next / previous match\n\n"

       "COMMAND PROMPT\n"
       "--------------\n"
       "C-b :        Open tmux command prompt\n\n"

       "HELP\n"
       "----\n"
       "C-b ?        List all keybindings\n\n"

       "INSPECTING TMUX SETTINGS (runtime)\n"
       "----------------------------------\n"
       "Show all options:\n"
       "  tmux show-options -g\n\n"
       "Show one option value:\n"
       "  tmux show-options -g status-left\n\n"
       "Show window options:\n"
       "  tmux show-window-options -g\n\n"
       "Show current keybindings:\n"
       "  tmux list-keys\n\n"
       "Check what command a key runs:\n"
       "  tmux list-keys | grep '<key>'\n\n"

       "Symbol    Meaning\n"
       "   *         Denotes the current window.\n"
       "   -         Marks the last window (previously selected).\n"
       "   #         Window activity is monitored and activits been detected.\n"
       "   !         Window bells are monitored and a bell has occurred in the window.\n"
       "   ~         The window has been silent for the monitor-silence interval.\n"
       "   M         The window contains the marked pane.\n"
       "   Z         The window's active pane is zoomed.\n\n"

       "Tip: tmux config is evaluated at startup,\n"
       "but you can reload it live with:\n"
       "  tmux source-file ~/.tmux.conf\n\n")

      (goto-char (point-min))
      (view-mode 1))

    (pop-to-buffer buf)))

PGCli

PostgreSQL CLI

;;;; gopar-pgcli.el — pgcli launcher with safety + UX

(require 'cl-lib)

(defun gopar/pgcli-parse-alias-dsn (&optional file)
  "Parse the [alias_dsn] section from pgcli config FILE.
Return alist of (ALIAS . DSN)."
  (let* ((file (or file (expand-file-name "~/.config/pgcli/config")))
         (lines (with-temp-buffer
                  (insert-file-contents file)
                  (split-string (buffer-string) "\n")))
         (in-section nil)
         (result '()))
    (dolist (line lines)
      (cond
       ((string-match-p "^\\[alias_dsn\\]" line)
        (setq in-section t))
       ((and in-section (string-match-p "^\\[" line))
        (setq in-section nil))
       ((and in-section
             (string-match
              "^\\([^#[:space:]]+\\)[[:space:]]*=[[:space:]]*\\(.+\\)$"
              line))
        (push (cons (match-string 1 line)
                    (match-string 2 line))
              result))))
    (nreverse result)))

;;; ------------------------------------------------------------
;;; UX helpers
;;; ------------------------------------------------------------

(defun gopar/pgcli-annotate-alias (alias)
  "Return ALIAS propertized for safety display."
  (cond
   ((string-match-p "prod" alias)
    (propertize alias 'face 'error))
   ((string-match-p "test" alias)
    (propertize alias 'face 'warning))
   (t
    (propertize alias 'face 'success))))

(defun gopar/pgcli--confirm-prod (dsn &optional alias)
  "Confirm before connecting to production."
  (when (or (and alias (string-match-p "prod" alias))
            (string-match-p "prod" dsn))
    (unless (yes-or-no-p "⚠️  Connect to PROD database? ")
      (user-error "Aborted prod connection"))))

(defun gopar/pgcli--safe-buffer-label (dsn &optional alias)
  "Generate a safe buffer label without passwords or hosts."
  (let* ((user (when (string-match "//\\([^:@/]+\\)" dsn)
                 (match-string 1 dsn)))
         (db   (when (string-match "/\\([^/?]+\\)$" dsn)
                 (match-string 1 dsn))))
    (format "%s@%s/%s"
            (or user "user")
            (or alias "custom")
            (or db "db"))))

(defun gopar/pgcli--run-vterm (dsn &optional alias)
  "Launch pgcli as the primary vterm process."
  (gopar/pgcli--confirm-prod dsn alias)
  (let* ((label (gopar/pgcli--safe-buffer-label dsn alias))
         (buffer-name (format "*pgcli: %s*" label))
         (vterm-buffer-name buffer-name)
         (vterm-shell (format "pgcli %s" (shell-quote-argument dsn)))
         (vterm-kill-buffer-on-exit nil))
    (unless (get-buffer buffer-name)
      (with-current-buffer (vterm)
        (setq-local vterm-max-scrollback 5000)))
    (pop-to-buffer buffer-name)))


;;; Main interactive command
(defun gopar/pgcli ()
  "Select pgcli alias or enter custom DSN, run in vterm."
  (interactive)
  (let* ((aliases (gopar/pgcli-parse-alias-dsn))
         (display-alist
          (mapcar (lambda (pair)
                    (cons (gopar/pgcli-annotate-alias (car pair))
                          pair))
                  aliases))
         (choices (mapcar #'car display-alist))
         (selection
          (completing-read
           "pgcli (alias or DSN): "
           choices
           nil nil))
         (pair (cdr (assoc selection display-alist))))
    (if pair
        (gopar/pgcli--run-vterm (cdr pair) (car pair))
      ;; Free-form DSN typed by user
      (gopar/pgcli--run-vterm selection "custom"))))

Private

Things that I want to keep private/out of git

End

(progn
  (add-to-list 'default-frame-alist `(font . "Hack 16"))
  (set-face-attribute 'default nil :font "Hack 16"))
;; (progn
;;   (add-to-list 'default-frame-alist `(font . "IBM Plex Mono 16"))
;;   (set-face-attribute 'default nil :font "IBM Plex Mono 16"))

;; (progn
;;   (add-to-list 'default-frame-alist `(font . "Fira Code 16"))
;;   (set-face-attribute 'default nil :font "Fira Code 16"))

;; (progn
;;   (add-to-list 'default-frame-alist `(font . "mononoki 16"))
;;   (set-face-attribute 'default nil :font "mononoki 16"))

;;; Local Variables: *** ;;; eval: (add-hook ‘after-save-hook #’org-babel-tangle nil t) *** ;;; End: ***

About

My Secret Sauce

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published