;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-

;;;; Misc settings

;; start Emacs as a server process
(server-start)

(setq doom-user-dir "~/.doom.d/")

;; package-archives
(setq package-archives
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")))

(after! projectile
  (setq projectile-project-root-files-bottom-up
        (remove ".git" projectile-project-root-files-bottom-up)))

;; Set fonts
(setq fontsize 18)
(setq monofontfam "JetBrainsMono Nerd Font Mono")
(setq doom-font (font-spec :family monofontfam :size fontsize)
      doom-variable-pitch-font (font-spec :family monofontfam :size fontsize)
      doom-symbol-font (font-spec :family monofontfam :size fontsize)
      doom-big-font (font-spec :family monofontfam :size fontsize))

;; Use catppuccin-mocha theme
(setq doom-theme 'catppuccin)
(setq catppuccin-flavor 'mocha)

;; History
(setq savehist-file "~/.local/share/emacs/savehist")
(savehist-mode 1)
(setq history-length t)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history 1)
(setq savehist-additional-variables
      '(kill-ring
        search-ring
        regexp-search-ring))

;; Display time in the modeline
(display-time-mode 1)

;; Enable blink cursor
(blink-cursor-mode 1)

;; Sentences end with a single space because it's right and proper
(setq sentence-end-double-space nil)

;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type 'relative)

;; Keybinding to kill-whole-line
(global-set-key (kbd "M-9") 'kill-whole-line)

;; trim newline from string output
(defun string-trim-final-newline (string)
  "Trim the last newline character from string.
Used with `shell-command-to-string'.
Source: https://emacs.stackexchange.com/a/21906"
  (let ((len (length string)))
    (cond
     ((and (> len 0) (eql (aref string (- len 1)) ?\n))
      (substring string 0 (- len 1)))
     (t string))))

;; wrapper around `shell-command-to-string' to remove newline
(defun shell-command-output-string (command)
  (string-trim-final-newline (shell-command-to-string command)))

;; Copy all or text selection
(defun xah-copy-all-or-region ()
  "Put the whole buffer content to `kill-ring', or text selection if there's one.
Respects `narrow-to-region'.
URL `https://ergomacs.org/emacs/emacs_copy_cut_all_or_region.html'
Version 2015-08-22"
  (interactive)
  (if (use-region-p)
      (progn
        (kill-new (buffer-substring (region-beginning) (region-end)))
        (message "Text selection copied."))
    (progn
      (kill-new (buffer-string))
      (message "Buffer content copied."))))

;; Cut all or text selection
(defun xah-cut-all-or-region ()
  "Cut the whole buffer content to `kill-ring', or text selection if there's one.
Respects `narrow-to-region'.
URL `https://ergomacs.org/emacs/emacs_copy_cut_all_or_region.html'
Version 2015-08-22"
  (interactive)
  (if (use-region-p)
      (progn
        (kill-new (buffer-substring (region-beginning) (region-end)))
        (delete-region (region-beginning) (region-end)))
    (progn
      (kill-new (buffer-string))
      (delete-region (point-min) (point-max)))))

;; open links in default browser
(setq browse-url-browser-function 'browse-url-default-browser)

;; after copy Ctrl+c in Linux X11, you can paste by `yank' in emacs
(setq select-enable-clipboard t)

;; after mouse selection copy in X11, you can paste by `yank' in emacs
(setq select-enable-primary t)

;; set keybinding for paste
(global-set-key (kbd "C-S-V") #'clipboard-yank)

;; Smart home key
(defun smart-beginning-of-line ()
  "Move point to first non-whitespace character or beginning-of-line.

Move point to the first non-whitespace character on this line.
If point was already at that position, move point to beginning of line."
  (interactive "^")
  (let ((oldpos (point)))
    (back-to-indentation)
    (and (= oldpos (point))
         (beginning-of-line))))

(global-set-key (kbd "<home>") 'smart-beginning-of-line)
(global-set-key (kbd "<end>") 'end-of-line)

;; Autoformat on save
(setq +format-on-save-enabled-modes
      '(emacs-lisp-mode))

;; UTF-8
(prefer-coding-system 'utf-8)
(when (display-graphic-p)
  (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))


;;;; undo-tree-mode - visualize undos and branches

(global-undo-tree-mode)
(setq undo-tree-visualizer-timestamps t)
(setq undo-tree-visualizer-diff t)
(setq undo-tree-history-directory-alist '(("." . "~/.local/share/emacs/backups/undo-tree")))


;;;; evil-nerd-commenter

(global-set-key (kbd "M-;") 'evilnc-comment-or-uncomment-lines)
(global-set-key (kbd "C-c l") 'evilnc-quick-comment-or-uncomment-to-the-line)
(global-set-key (kbd "C-c c") 'evilnc-copy-and-comment-lines)
(global-set-key (kbd "C-c p") 'evilnc-comment-or-uncomment-paragraphs)


;;;; org-mode

(add-hook 'org-mode-hook (lambda () (org-superstar-mode 1)))
(require 'org-protocol)

(setq org-modules '(org-agenda
                    org-choose
                    org-collector
                    ))
(eval-after-load 'org
  '(org-load-modules-maybe t))

(setq org-directory "~/sync/org/")
(setq org-default-notes-file "~/sync/org/inbox.org")
(setq org-agenda-files '("~/sync/org/inbox.org"))

;; support selecting lines by using shift
(setq org-support-shift-select t)

;; org-mode tags
;; I hardly use these but whatever
(setq org-tag-alist (quote (("@archiving" .?a)
                            ("@blog" .?b)
                            ("@homelab" . ?l)
                            ("@hyperreal.coffee" .?h)
                            ("@reading" . ?r)
                            ("@selfcare" . ?s))))

;; org-mode keybindings
(with-eval-after-load 'org
  (bind-key "C-c $" 'org-archive-subtree)
  (bind-key "C-c r" 'org-capture)
  (bind-key "C-c R" 'org-roam-capture)
  (bind-key "C-c a" 'org-agenda)
  (bind-key "C-c l" 'org-store-link)
  (bind-key "C-c L" 'org-insert-link-global)
  (bind-key "C-c O" 'org-open-at-point-global))

(setq org-use-effective-time t)

;; org-refile
(setq org-reverse-note-order nil) ; new notes prepended
(setq org-refile-use-outline-path 'full-file-path)
(setq org-outline-path-complete-in-steps nil)
(setq org-refile-allow-creating-parent-nodes nil)
(setq org-refile-use-cache nil)
(setq org-blank-before-new-entry '((heading . t) (plain-list-item . t)))

(setq org-refile-targets
      `((("~/sync/org/inbox.org"
          "~/sync/org/archiving.org"
          "~/sync/org/computing.org"
          "~/sync/org/reading.org")
         . (:maxlevel . 5))))

;; org-todo-keywords
(with-eval-after-load 'org
  (setq org-todo-keywords
        '((sequence
           "STARTED(s)"
           "TODO(t)"
           "WAITING(w)"
           "IDEA(i)"
           "SOMEDAY(.)"
           "BLOCKED(k)"
           "|"
           "DONE(x)"
           "CANCELLED(c)")
          (sequence "PROJECT" "|" "DONE(x)")
          (sequence "MAYBE(,0)" "CHOSEN(c,+)" "|" "REJECTED")))

  ;; catppuccin palette
  (setq org-todo-keyword-faces
        '(("STARTED" . (:foreground "#a6d189" :weight bold))
          ("TODO" . (:foreground "#a6e3a1" :weight bold))
          ("WAITING" . (:foreground "#f9e2af" :weight bold))
          ("IDEA" . (:foreground "#74c7ec" :weight bold))
          ("SOMEDAY" . (:foreground "#f2cdcd" :weight bold))
          ("BLOCKED" . (:foreground "#c6d0f5" :weight bold))
          ("DONE" . (:foreground "#f38ba8" :weight bold))
          ("CANCELLED" . (:foreground "#e78284" :weight bold))
          ("MAYBE" . (:foreground "#c6d0f5" :weight bold))
          ("CHOSEN" . (:foreground "#89b4fa" :weight bold))
          ("REJECTED" . (:foreground "#d20f39" :weight bold)))))

(setq org-log-done 'time)

;; ox-hugo
(with-eval-after-load 'ox
  (require 'ox-hugo))

;; copy images from clipboard into org file with yank-media
(defun org-mode--image-yank-handler (type image)
  (let ((file (read-file-name (format "Save %s image to:" type))))
    (when (file-directory-p file)
      (user-error "%s is a directory"))
    (when (and (file-exists-p file)
               (not (yes-or-no-p (format "%s exists; overwrite?" file))))
      (user-error "%s exists"))
    (with-temp-buffer
      (set-buffer-multibyte nil)
      (insert image)
      (write-region (point-min) (point-max) file))
    (insert (format "[[file:%s]]\n" (file-relative-name file)))))

(add-hook 'org-mode-hook (lambda () (yank-media-handler "image/.*" #'org-mode--image-yank-handler)))

;;;; org-roam
(setq org-roam-directory "~/sync/org-roam")
(org-roam-db-autosync-mode)

;; org-roam-ui
(use-package! websocket
  :after org-roam)

(use-package! org-roam-ui
  :after org-roam
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow nil
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

;; colorize compilation buffer
;; from https://sachachua.com/dotemacs/index.html#orga33bac5
(require 'ansi-color)
(defun colorize-compilation-buffer ()
  (when (eq major-mode 'compilation-mode)
    (let ((inhibit-read-only t))
      (ansi-color-apply-on-region compilation-filter-start (point-max)))))
(add-hook 'compilation-filter-hook 'colorize-compilation-buffer)


;;;; helpful

(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)
(global-set-key (kbd "C-h x") #'helpful-command)
(global-set-key (kbd "C-c C-d") #'helpful-at-point)


;;;; use bufler instead of IBuffer
(global-set-key (kbd "C-x C-b") #'bufler-list)


;;;; dictionary

(global-set-key (kbd "C-c l") #'dictionary-lookup-definition)
(setq dictionary-server "dict.org")


;;;; casual

(map! :after dired
      :map dired-mode-map
      "C-o" #'casual-dired-tmenu)

(require 'casual-bookmarks)
(keymap-set bookmark-bmenu-mode-map "C-o" #'casual-bookmarks-tmenu)


;;;; elpher

(setq elpher-default-url-type "gemini")


;;;; vterm

(setq vterm-kill-buffer-on-exit t)
(setq vterm-always-compile-module t)
(setq vterm-other-window t)
(setq vterm-buffer-name-string "vterm %s")
(setq vterm-copy-exclude-prompt t)
(setq vterm-ignore-blink-cursor nil)


;;;; mastodon.el

(setq mastodon-instance-url "https://tilde.zone"
      mastodon-active-user "hyperreal"
      mastodon-use-emojify t)


;;;; elfeed

(setq elfeed-use-curl t)
(setq elfeed-protocol-enabled-protocols '(fever))
(elfeed-protocol-enable)
(setq elfeed-protocol-fever-update-unread-only t)
(setq elfeed-protocol-fever-fetch-category-as-tag t)
(setq elfeed-protocol-feeds '(("fever+https://hyperreal@freshrss.hyperreal.coffee"
                               :api-url "https://freshrss.hyperreal.coffee/api/fever.php"
                               :password (password-store-get "freshrss"))))


;;;; open org files on startup

(find-file (file-name-concat org-directory "inbox.org"))
(find-file (file-name-concat org-directory "reading.org"))
(find-file (file-name-concat org-directory "computing.org"))
(find-file (file-name-concat org-directory "archiving.org"))