;;; init.el --- Emacs configuration by chase. -*- lexical-binding: t -*-

;;; Commentary:

;;; Code:
;; Check Emacs version and warn if it is not supported
(when (and (<= emacs-major-version 29) (< emacs-minor-version 4))
  (warn (format "This config is designed for Emacs 29+, please upgrade.")))

;; display startup time
(defun chase/display-startup-time ()
  "Write init time and number of garbage collections to message buffer."
  (message "Emacs loaded in %s with %d garbage collections"
		   (emacs-init-time) gcs-done))

(add-hook 'emacs-startup-hook #'chase/display-startup-time)

;; automatically create missing directories
;; source: https://emacsredux.com/blog/2022/06/12/auto-create-missing-directories/
(defun er-auto-create-missing-dirs ()
  "Create missing parent directories automatically when opening a file."
  (let ((target-dir (file-name-directory buffer-file-name)))
    (unless (file-exists-p target-dir)
      (make-directory target-dir t))))

(add-to-list 'find-file-not-found-functions #'er-auto-create-missing-dirs)

;; no bars
(add-hook 'window-setup-hook (lambda ()
                               (menu-bar-mode -1)
                               (tool-bar-mode -1)
                               (scroll-bar-mode -1)))

;; bootstrap straight
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(setq straight-use-package-by-default t)

(setq use-package-always-demand t
      use-package-compute-statistics t)

(use-package bind-key)

(use-package diminish)

;; benchmark
(use-package benchmark-init
  :config
  (add-hook 'after-init-hook #'benchmark-init/deactivate))

;; set locale
(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8-unix)
(setq-default buffer-file-coding-system 'utf-8-unix)

;; a few basic config points
(setq use-short-answers t)
(setq native-comp-async-report-warnings-errors 'silent)

;; date & time stuff
(use-package calendar
  :config
  (setq calendar-week-start-day 1)) ;; Monday

(use-package time
  :after doom-modeline
  :init
  (setq display-time-default-load-average nil
		display-time-24hr-format t)
  (display-time-mode))

;; battery
(use-package fancy-battery
  :after doom-modeline)

;; anzu
(use-package anzu
  :config
  (global-anzu-mode))

;; become evil
(use-package evil
  :init
  (setq evil-want-keybinding nil
		evil-want-integration t
		evil-respect-visual-line-mode t
		evil-undo-system 'undo-redo
		evil-want-fine-undo t)
  :config
  (evil-mode))

(use-package evil-anzu
  :after (evil anzu))

(use-package evil-collection
  :after evil
  :diminish evil-collection-unimpaired-mode
  :config
  (evil-collection-init))

(use-package evil-surround
  :config
  (global-evil-surround-mode 1))

(use-package vertico
  :custom
  (vertico-cycle t)
  :init
  (vertico-mode))

(use-package savehist
  :init
  (savehist-mode))

(use-package orderless
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion)))))

;; prevent laggy icons
(setq inhibit-compacting-font-caches t)

;; make Emacs pretty
(use-package nerd-icons
  :custom
  (nerd-icons-font-family "BlexMono Nerd Font"))

(use-package nerd-icons-completion
  :config
  (nerd-icons-completion-mode))

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

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

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

;; theme stuff
(use-package catppuccin-theme
  :config
  (load-theme 'catppuccin :no-confirm)
  (add-hook 'server-after-make-frame-hook #'catppuccin-reload))

;; colourize colour names
(use-package rainbow-mode
  :ensure nil ;; built-in package
  :hook
  (org-mode . rainbow-mode)
  (text-mode . rainbow-mode))

;; make parentheses colourful
(use-package rainbow-delimiters
  :hook
  (prog-mode . rainbow-delimiters-mode))

;; misc aesthetic settings
(global-hl-line-mode)
(column-number-mode)

;; line numbers, not quite everywhere
(setq-default display-line-numbers-type 'relative)
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
(add-hook 'text-mode #'display-line-numbers-mode)

;; move cursor to new help windows
(setq help-window-select t)

;; less blinky cursor
(setq blink-cursor-blinks 3)

;; move between windows easier
(windmove-default-keybindings)

;; doom-modeline
(use-package doom-modeline
  :hook
  (after-init . doom-modeline-mode))

;; tab behaviour
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

;; electric pair
(electric-pair-mode t)

;; use ibuffer instead of Buffer Menu, it looks nicer
(require 'ibuf-ext)

(global-set-key (kbd "C-x C-b") 'ibuffer)

(setq ibuffer-saved-filter-groups
	  '(("Default"
         ("Terminal" (name . "^\\*terminal\\*$"))
         ("Nix"      (mode . nix-ts-mode))
		 ("Code"     (or
				      (mode . c-ts-mode)
				      (mode . c++-ts-mode)
                      (mode . emacs-lisp-mode)
				      (mode . js-ts-mode)
                      (mode . makefile-gmake-mode)
				      (mode . python-ts-mode)
				      (mode . rust-ts-mode)))
		 ("Org"      (mode . org-mode))
		 ("Dired"    (mode . dired-mode))
		 ("Magit"    (name . "^magit"))
		 ("Emacs"    (or
				      (name . "^\\*Completions\\*$")
				      (name . "^\\*Help\\*$")
				      (name . "^\\*info\\*$")
				      (name . "^\\*Messages\\*$")
				      (name . "^\\*scratch\\*$"))))))

(add-hook 'ibuffer-mode-hook
		  (lambda ()
			(ibuffer-switch-to-saved-filter-groups "Default")
            (ibuffer-auto-mode 1)))

;; make dired less messy
(setf dired-kill-when-opening-new-dired-buffer t)

;; kill buffers
(substitute-key-definition 'kill-buffer 'kill-buffer-and-window global-map)

;; git stuff
(use-package magit
  :config
  (setq magit-bury-buffer-function 'kill-buffer))

(use-package magit-todos
  :after magit)

(use-package forge
  :after magit
  :init
  (setq forge-add-default-bindings nil))

(use-package git-modes)

(use-package editorconfig
  :config
  (editorconfig-mode 1))

;; treemacs stuff
(use-package treemacs
  :bind (:map global-map
			  ("M-0" . treemacs-select-window)
			  ("C-x t t" . treemacs))
  :config
  (setq treemacs-persist-file "~/.cache/emacs/treemacs-persist"
		treemacs-width 25))

(use-package treemacs-evil
  :after (treemacs evil))

(use-package treemacs-magit
  :after (treemacs magit))

(use-package treemacs-nerd-icons
  :after (treemacs nerd-icons)
  :config
  (treemacs-load-theme "nerd-icons"))

;; corfu
(use-package corfu
  :custom
  (corfu-cycle t)
  (corfu-auto t)
  (corfu-auto-prefix 2)
  (corfu-auto-delay 0.2)
  (corfu-quit-at-boundary 'separator)
  (corfu-echo-documentation 0.25)
  :init
  (when (>= emacs-major-version 30)
    (global-completion-preview-mode 1))
  (unless (display-graphic-p)
    (when (require 'corfu-terminal nil :noerror)
      (corfu-terminal-mode +1)))
  (global-corfu-mode 1)
  (corfu-history-mode 1))

;; license templates
(use-package license-templates)

;; yasnippet config
(use-package yasnippet
  :defer t
  :hook
  (prog-mode . yas-minor-mode)
  :config
  (setq yas-snippet-dirs
		'((expand-file-name "snippets/" user-emacs-directory) ;; personal
          (expand-file-name "work/" user-emacs-directory))))  ;; keep em separated

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

;; c
(use-package c-ts-mode
  :straight nil
  :defer t
  :config
  (setq c-ts-mode-indent-style 'k&r)
  (setq c-ts-mode-indent-offset 4))

;; python
(use-package python
  :defer t
  :config
  (setq python-flymake-command '("ruff" "--quiet" "--stdin-filename=stdin" "-")))

;; eglot
(use-package eglot
  :straight nil
  :hook
  (c-ts-mode . eglot-ensure)
  (nix-ts-mode . eglot-ensure)
  (python-ts-mode . eglot-ensure)
  :config
  (setq eldoc-echo-area-display-truncation-message nil
	    eldoc-echo-area-prefer-doc-buffer t
	    max-mini-window-height 2)
  (dolist (mode '(((nix-mode nix-ts-mode) . ("nixd"))))
    (add-to-list 'eglot-server-programs mode)))

;; treesit-auto
(use-package treesit-auto
  :custom
  (treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  (setq treesit-font-lock-level 4)
  (global-treesit-auto-mode))
  
;; CSV mode
(use-package csv-mode
  :defer t)

;; markdown
(use-package markdown-mode
  :defer t
  :mode
  ("README\\.md\\'" . gfm-mode)
  :config
  (setq markdown-gfm-uppercase-checkbox t
		markdown-enable-wiki-links t))

;; powershell support
(use-package powershell
  :defer t)

;; yaml
(use-package yaml
  :defer t)

;; portage config for gentoo
(use-package portage-modes
  :defer t)

;; pdf tools
(use-package pdf-tools
  :defer t
  :pin manual ;; don't reinstall when package updates
  :mode
  ("\\.pdf\\'" . pdf-view-mode)
  :hook
  (pdf-view-mode-hook . (internal-show-cursor nil nil))
  :config
  (setq-default pdf-view-display-size 'fit-page)
  (setq pdf-annot-activate-created-annotations t)
  (pdf-tools-install :no-query)
  (require 'pdf-occur))

;; org stuff
(use-package org
  :mode
  ("\\.\\(org\\|org_archive\\|txt\\)\\'" . org-mode)
  :hook
  (org-mode . visual-line-mode)
  (org-mode . (lambda () (add-hook 'after-save-hook #'org-babel-tangle :append :local)))
  :diminish visual-line-mode
  :config
  ;; looks
  (setq org-ellipsis "⤵"
		org-hide-emphasis-markers t
		org-startup-folded 'content
		org-src-fontify-natively t
		org-edit-src-content-indentation 0)

  ;; keybinds
  (define-key global-map "\C-cl" #'org-store-link)
  (define-key global-map "\C-ca" #'org-agenda)
  (global-set-key (kbd "C-c c") 'org-capture)

  ;; agenda
  (require 'find-lisp)
  (unless (file-exists-p "~/Documents/org")
    (make-directory "~/Documents/org" t))
  (setq org-directory "~/Documents/org")
  (unless (file-exists-p (expand-file-name "gtd/" org-directory))
    (make-directory (expand-file-name "gtd/" org-directory) t))
  (setq chase/org-agenda-directory
        (expand-file-name "gtd/" org-directory))
  (setq org-agenda-files
        (find-lisp-find-files chase/org-agenda-directory "\.org$"))

  (setq org-capture-templates
		'(("i" "Inbox" entry (file "gtd/inbox.org")
		   "* TODO %?\n")
		  ("n" "Note" entry (file "gtd/inbox.org")
		   "* %? :NOTE:\n")))

  ;; TODO
  (setq org-log-done 'time)
  (setq org-todo-keywords
		'((sequence "TODO(t)" "NEXT(n)" "HOLD(h)" "|" "DONE(d)" "|" "Cancelled(c)")))

  (setq org-tag-alist '(("@errand" . ?e)
                        ("@office" . ?o)
                        ("@home" . ?h)))

  (setq org-refile-allow-creating-parent-nodes 'confirm
        org-refile-targets '(("projects.org" . (:level . 1))))

  ;; source blocks
  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
  (add-to-list 'org-structure-template-alist '("py" . "src python"))
  (add-to-list 'org-structure-template-alist '("sh" . "src shell"))
  (add-to-list 'org-structure-template-alist '("ts" . "src typescript"))
  (add-to-list 'org-structure-template-alist '("json" . "src json"))
  (add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))

  ;; babel
  (add-to-list 'org-babel-load-languages '(shell . t))

  ;; fix yaml fontify
  (add-to-list 'org-src-lang-modes '("yaml" . yaml-ts))

  ;; export
  (setq org-export-with-toc nil
		org-export-with-section-numbers nil)

  ;; LaTeX
  ;;(setq org-latex-compiler "xelatex")
  ;;(setq org-latex-listings 'minted
		;;org-latex-pdf-process
		;;'("latexmk -shell-escape -f -pdf -%latex -interaction=nonstopmode -output-directory=%o %f"))

  (use-package evil-org
	:after org
	:hook
	(org-mode . evil-org-mode)
	:diminish evil-org-mode
	:init
	(setq evil-want-C-i-jump nil)
    :config
	(require 'evil-org-agenda)
	(evil-org-set-key-theme '(navigation insert textobjects additional calendar))
	(evil-org-agenda-set-keys))

  (use-package org-roam
    :after org
	:bind
	(("C-c n l" . org-roam-buffer-toggle)
     ("C-c n f" . org-roam-node-find)
     ("C-c n g" . org-roam-graph)
     ("C-c n i" . org-roam-node-insert)
     ("C-c n c" . org-roam-capture)
     ;; Dailies
     ("C-c n j" . org-roam-dailies-capture-today))
	:config
    (unless (file-exists-p (expand-file-name "roam/" org-directory))
      (make-directory (expand-file-name "roam/" org-directory) t))
	(setq org-roam-directory
     (expand-file-name "roam/" org-directory))
    (require 'org-roam-protocol)
    (require 'org-roam-export)
	(org-roam-db-autosync-mode)
    (setq org-roam-capture-templates
          '(("m" "main" plain "%?"
             :if-new
             (file+head "main/${slug}.org" "#+title: ${title}\n")
             :unnarrowed t)
            ("r" "reference" plain "%?"
             :if-new
             (file+head "reference/${title}.org" "#+title: ${title}\n")
             :unnarrowed t)))

    (setq org-roam-capture-ref-templates
          '(("r" "ref" plain "%?" :target
             (file+head "reference/${slug}.org" "#+title: ${title}\n${body}\n")
             :unnarrowed t))))

  (use-package org-superstar
	:after org
	:hook
	(org-mode . org-superstar-mode))

  (use-package org-appear
	:after org
	:hook
	(org-mode . org-appear-mode)
    :config
    (setq org-appear-autolinks t))

  ;; GitHub flavoured markdown export
  (use-package ox-gfm
	:after org)

  ;; hugo markdown export
  (use-package ox-hugo
    :after org)

  ;; HTML syntax highlighting
  (use-package htmlize
	:after org)

  (use-package ox-moderncv
    :straight nil
    :load-path "~/org-cv/"
    ;;(org-cv :type git :host gitlab :repo "Titan-C/org-cv"))
    :init (require 'ox-moderncv))


  (use-package ox-hugocv
    :after org
    :straight
    (org-cv :type git :host gitlab :repo "Titan-C/org-cv")))

;; spell checking
(use-package ispell
  :custom
  (ispell-program-name "aspell")
  (ispell-silently-savep t))

(use-package flyspell
  :defer t
  :hook
  (markdown-mode . flyspell-mode)
  (org-mode . flyspell-mode)
  :diminish flyspell-mode)

;; flymake config
(use-package flymake
  :straight nil
  :defer t
  :init
  (setq elisp-flymake-byte-compile-load-path load-path)
  :bind (:map global-map
		      ("M-n" . flymake-goto-next-error)
		      ("M-p" . flymake-goto-prev-error)))

;; windows printer
(setq ps-printer-name t)
(setq ps-lpr-command "C:/Program Files/gs/gs10.03.1/bin/gswin64c.exe")
(setq ps-lpr-switches '("-q" "-dNOPAUSE" "-dBATCH" "-sDEVICE=mswinpr2"
						"-sPAPERSIZE=a7"))

(use-package emms)

(provide 'init)
;;; init.el ends here
