;;; init.el --- my emacs configuration  -*- lexical-binding: t; -*-

;;; Commentary:

;; (profiler-start 'cpu+mem)

;; Things we have, or want to have, in this configuration:
;; - Make a nice interface
;; - Programming
;; - Offline web browsing
;; - Reading mail
;; - Sending mail
;; - Bibliography management
;; - Taking notes

(add-to-list 'load-path (expand-file-name (concat user-emacs-directory "site-lisp")))

(add-to-list 'exec-path "/home/noa/.config/Scripts")

(use-package use-package
  :custom
  (use-package-compute-statistics t)
  (use-package-enable-imenu-support t))

;; Give me stats on garbage collection and startup time
(add-hook 'emacs-startup-hook
	  #'(lambda ()
	      (message (format "Initialised in %s seconds with %s garbage collections." (emacs-init-time) gcs-done))))

;; keep the emacs directory clean
(use-package no-littering)

;; It is often useful to be able to run a command while i am already in the process of running a command in the minibuffer.  This is rarely two extended commands; usually it is completion.
(use-package mb-depth
  :hook (after-init . minibuffer-depth-indicate-mode))

(use-package tubthumping-theme
  :disabled t
  :load-path "~/Documents/Projects/Tubthumping/"
  :config
  (load-theme 'tubthumping t))

;;; Fonts
;; Using a proportional font is the right way to do things, but emacs is very old and comes from a time before the innovation of legibility.  As a result, there are some things that require a monospaced font, so i set one here.
(custom-set-faces
 '(default ((t (:family "Noto Sans Mono" :height 120))))
 '(variable-pitch ((t (:family "Noto Sans" :height 120))))
 '(fixed-pitch ((t (:family "Noto Sans Mono" :height 120))))
 )

(use-package face-remap
  :hook
  ((text-mode . variable-pitch-mode)
   (prog-mode . variable-pitch-mode)))

;; Use simplified not korean hanzi
(when (display-graphic-p)
  (dolist (charset '(han cjk-misc))
    (set-fontset-font t charset (font-spec :family "Noto Serif CJK SC"))))

;; Prioritise fsd and noto emoji over coloured variants
(when (and (display-graphic-p)
           (member "FSD Emoji" (font-family-list)))
  (set-fontset-font t 'emoji (font-spec :family "FSD Emoji") nil 'prepend))
(when (and (display-graphic-p)
           (member "Noto Emoji" (font-family-list)))
  (set-fontset-font t 'emoji (font-spec :family "Noto Emoji") nil 'append))

;; While we're here, let's set up emoji input.
(use-package emoji
  :bind ("C-." . emoji-search))

;; Smooth-ish scrolling with the mouse, and smooth scrolling with C-v and M-v.  Sadly it doesn't work with C-l.
(use-package pixel-scroll
  :hook (after-init . pixel-scroll-precision-mode)
  :custom
  (pixel-scroll-precision-use-momentum t)
  (pixel-scroll-precision-interpolate-page t)
  :bind (([remap scroll-up-command] . pixel-scroll-interpolate-down)
         ([remap scroll-down-command] . pixel-scroll-interpolate-up)))

;; Update the calendar.  We want weeks to start on a monday, the first day of the week.  Holidays should be highlighted, and the date format should put the year first.
(use-package calendar
  :defer t
  :custom
  (calendar-week-start-day 1)
  (calendar-date-style 'iso)

  :config
  ;; Ensure the calendar always displays at the bottom of the screen, rather than wrapping weirdly and looking bad when it shows up in a side window.
  (add-to-list 'display-buffer-alist
             '("\\*Calendar\\*"
               (display-buffer-in-side-window)
               (side . bottom))))

;; Squeeze text into a more legible sliver.
(use-package visual-fill-column
  :custom
  (visual-fill-column-enable-sensible-window-split t)
  (visual-fill-column-fringes-outside-margins nil) ; Keep fringes on the inside so relevant icons are in the right place
  (visual-fill-column-center-text t)
  :hook
  ((text-mode . visual-fill-column-mode)
   (eww-after-render . visual-line-fill-column-mode))
  )

(use-package simple
  :hook ((text-mode . visual-line-mode)
         (after-init . auto-save-mode))
  :custom
  (save-interprogram-paste-before-kill t)
  (kill-ring-max 25)
  ;; Hide commands in M-x which do not work in the current mode.
  (read-extended-command-predicate 'command-completion-default-include-p)
  ;; My brain is not ready for advanced emacs undo
  (undo-no-redo t)
  :bind (("C-z" . undo)
         ("C-S-z" . undo-redo)))

;; (advice-add 'text-scale-adjust :after #'visual-fill-column-adjust)

;; Doesn't indent nicely with proportional fonts
;; (use-package adaptive-wrap :ensure t)
;; (add-hook 'visual-fill-column-mode-hook #'visual-wrap-prefix-mode)

;; Make tooltip information appear consistently in the echo area
(use-package tooltip
  :custom (tooltip-mode nil))

;; startup.el does not provide itself, cannot be used with use-package
(setopt inhibit-startup-screen t)

(use-package mouse
  :custom
  (mouse-yank-at-point t)
  (mouse-drag-and-drop-region nil)
  (mouse-yank-at-point t)
  ;; Allow for slightly sloppier clicking.
  (mouse-prefer-closest-glyph t) ;; Emacs 30
)

;; Shift click to select region with the mouse (https://christiantietze.de/posts/2022/07/shift-click-in-emacs-to-select/)
(global-set-key (kbd "S-<down-mouse-1>") #'mouse-set-mark)
;; Doesn't work great with a proportional font but could still be useful.
(global-set-key (kbd "C-S-<down-mouse-1>") #'mouse-drag-region-rectangle)

;; Markdown
(use-package markdown-mode
  ;; :ensure t
  :mode ("\\.\\(?:md\\|markdown\\|mkd\\|mdown\\|mkdn\\|mdwn\\)\\'" . markdown-mode)
  :custom
  (markdown-disable-tooltip-prompt t) ; When inserting a link, only prompt for url and link text
  (markdown-enable-html nil) ; I don't believe markdown should have html
  (markdown-hide-urls t) ; Make inline urls look a bit neater
  (markdown-url-compose-char ?~)
  (markdown-max-image-size '(640 . 480))

  ;; :hook ((markdown-mode . noa/set-buffer-name-to-markdown-title))
  )

;; Use abbrev as a simple autocorrect mode.  M-x list-abbrevs for stats.
;; Check  ~/.config/emacs/abbrev_defs to update any mistakes
(use-package abbrev
  :hook (text-mode . abbrev-mode))

(use-package flyspell
  :hook (text-mode . flyspell-mode)
  :custom
  (flyspell-abbrev-p t))

(use-package writegood-mode
  :hook (text-mode . writegood-mode))

;; A nicer package for spellchecking
(use-package jinx
  :disabled t
  ;; :ensure t
  :hook (text-mode . jinx-mode)
  :commands (jinx-correct jinx-correct-all)
  :config
  (put 'jinx-overlay 'mouse-face '(jinx-misspelled highlight)) ;; Use standard mouse face for jinx overlays
  (add-to-list 'jinx-exclude-regexps '(t "\\cc")) ;; Don't check chinese characters
  )

;; Automatically creates an abbrev for words i correct, so i don't make the same mistake twice
(defun jinx--add-to-abbrev (overlay word)
  "Add abbreviation to `global-abbrev-table'.
The misspelled word is taken from OVERLAY.  WORD is the corrected word."
  (let ((abbrev (buffer-substring-no-properties
                 (overlay-start overlay)
                 (overlay-end overlay))))
    (message "Abbrev: %s -> %s" abbrev word)
    (define-abbrev global-abbrev-table abbrev word)))
(advice-add 'jinx--correct-replace :before #'jinx--add-to-abbrev)

(use-package help
  :defer t
  :custom
  ;; When opening source files from a help window, use the same window
  (help-window-keep-selected t))

;; Despite the name, eww is a delight to use for text-heavy websites.
(use-package browse-url
  :defer t
  :custom
  (browse-url-browser-function 'noa/record-url)
	(browse-url-secondary-browser-function 'browse-url-default-browser))

;; I've had no use for cookies in eww
(use-package url-cookie
  :defer t
  :custom
  (url-cookie-trusted-urls '())
  (url-cookie-untrusted-urls '(".*")))

(use-package shr
  :defer t
  :custom
  (shr-cookie-policy nil)
  (shr-use-colors nil) ;; I like the colours i already have set
  (shr-max-width nil) ;; Don't insert hard linebreaks to wrap paragraphs
  (shr-bullet " • ")
  (shr-discard-aria-hidden t))

(use-package eww
  :defer t

  :custom
  ;; Better buffer naming for eww buffers.  See the help for a function which gives the page title and the url.
  (eww-auto-rename-buffer 'title))

;; Goto address mode makes urls and email address in a buffer clickable.  I want these clickable links to look like links, because that's what they are.  The two mouse face variables are what face is used on hover, which at the moment i ignore.  It might also be worth setting them to 'highlight.
(use-package goto-addr
  :hook (after-init . global-goto-address-mode)
  :custom
  (goto-address-mail-face 'link)
  (goto-address-mail-mouse-face 'highlight)
  (goto-address-url-face 'link)
  (goto-address-url-mouse-face 'highlight))

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

(use-package emacs
  :custom
  (window-combination-resize t)
  (window-resize-pixelwise t)
  (frame-resize-pixelwise t)
  (shell-file-name "/bin/sh")
  (completion-ignore-case t)
  (read-buffer-completion-ignore-case t)
  ;; yes-or-no-p uses y-or-no-p, and read-answer accepts single key press answers
  (use-short-answers t)
  (history-length 250)
  ;; Use a bar cursor and blink it and don't stop blinking it.
  (cursor-type 'bar)
  ;; Support opening new minibuffers from inside existing minibuffers.
  (enable-recursive-minibuffers t)
  ;; Whether to drop into the debugger on any error.  This seems cool, but in practice is a bit annoying.
  (debug-on-error nil)
  (create-lockfiles nil)
  (auto-save-interval 6)  ;; every six keystrokes
  (auto-save-timeout 5) ;; every 5 seconds
  (auto-save-no-message t)
  ;; Always give me a line of text when i'm near the edges of the screen
  (scroll-margin 2))

(use-package dired
  :defer t
  :custom
  (dired-recursive-deletes 'always) ;; Don't ask me things
  (dired-recursive-copies 'always) ;; Don't ask me things
  (dired-dwim-target t) ;; Synergy between two dired windows
  ;; I have quite a bit of storage and also make bad decisions regularly
  ;; People who have used computers in the last forty years or so will likely be familiar with this innovation.
  (delete-by-moving-to-trash t)



  ;; -l is required by dired
  ;; -a to also show dotfiles
  ;; -v to sort numbers properly
  ;; -F to visually distinguish different types of file
  ;; -h for human readable file sizes
  ;; I won't explain the meaning of the long flag.
  (dired-listing-switches "-alvFh --group-directories-first")

;; By default, don't show dired details
  :hook ((dired-mode . dired-hide-details-mode)
         (dired-mode . hl-line-mode))
  )

;; Show the recursive size of directories
(use-package dired-du
  :disabled t
  :hook (dired-mode . dired-du-mode)
  :after dired
  :custom
  (dired-du-size-format t))

;; Some tramp settings.
(use-package files
  :custom
  (view-read-only t)
  (confirm-kill-emacs 'y-or-n-p)
  ;; We are on a unix system, so it makes sense to end files in the unix system way.  I'm surprised this isn't the default.
  (require-final-newline t)

  ;; This behaviour changes how we visit symlinks.
  (find-file-visit-truename t)

  (remote-file-name-inhibit-locks t)
  (remote-file-name-inhibit-auto-save t)
  (remote-file-name-inhibit-auto-save-visited t)
  (backup-directory-alist '(("." . "~/.config/emacs/backups/")))
  (make-backup-files nil)
  (backup-by-copying t)
  (version-control t)
  ;; this will auto save to the current file
  (auto-save-visited-mode t)
  (auto-save-visited-interval 0.1)
  (save-silently t) ;; don't message when saving
  )
(use-package tramp
  :defer t
  :custom
  (tramp-inline-compress-start-size 1000)
  (tramp-verbose 3))
;; (add-to-list 'tramp-remote-path 'tramp-own-remote-path)

;; I only use git, and this apparently speeds up tramp.
(use-package vc-hooks
  :after vc
  :custom
  (vc-handled-backends '(Git)))

;;  It seems that tramp can also be made faster with these .ssh/config settings.

;; Host *
;;      ControlMaster auto
;;      ControlPath ~/.ssh/master-%h:%p
;;      ControlPersist 10m
;;      ForwardAgent yes
;;      ServerAliveInterval 60

;; Don't override display actions when i manually initiate a buffer switch.
(use-package window
  :custom
  ;; My computer has a small screen, so i find that it's more beneficial for me to split the frame into columns, so i get more context.  However, splitting in this way only gives me a (window-width) of 61, so emacs will always split into vertically stacked windows.  By setting this to 80, the first split should always be vertical.
  (split-width-threshold 80)
  (switch-to-buffer-obey-display-actions t)
  ;; C-l from top to bottom
  (recenter-positions '(top middle bottom)))

(use-package frame
  :hook (after-init . window-divider-mode)
  :custom
  (window-divider-default-right-width 1)
  (window-divider-default-bottom-width 1)
  (window-divider-default-places t))

;; Taken from configuration for the vertico stack:
;; Add prompt indicator to `completing-read-multiple'.
;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
(defun crm-indicator (args)
  (cons (format "[CRM%s] %s"
		(replace-regexp-in-string
		 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
		 crm-separator)
		(car args))
	(cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

(use-package recentf
  :hook (after-init . recentf-mode)
  :custom
  (recentf-max-menu-items 25)
  (recentf-save-file "~/.config/emacs/recentf"))

(use-package bookmark
  :defer t
  :custom
  (bookmark-default-file "~/.config/emacs/bookmarks"))

;; Unfill commands
(defun unfill-paragraph ()
  "Takes a multi-line paragraph and makes it into a single line of text."
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))
(global-set-key (kbd "M-Q") #'unfill-paragraph)

;; Remember my position in files
(use-package saveplace
  :hook (after-init . save-place-mode))

;; Disable disabled commands
(setq disabled-command-function nil)

;; Don't save changes in the customize interface
(use-package cus-edit
  :custom
  (custom-file (make-temp-file "custom")))

;; Scroll along with text in compilation mode, and stop scrolling at the first error.
(use-package compile
  :defer t
  :custom
  (compilation-scroll-output 'first-error))

;; Don't advertise gnu on startup
(setq inhibit-startup-echo-area-message "noa")  ;; #userfreedom

;; Better buffer naming
(use-package uniquify
  :custom
  (uniquify-after-kill-buffer-p t)
  (uniquify-buffer-name-style 'forward)
  (uniquify-ignore-buffers-re "^\\*")
  (uniquify-separator "/"))

;; Emacs server
;; (unless (server-running-p) (server-start)))

;; Better support for long lines.
(use-package so-long
  :hook (after-init . global-so-long-mode))

;; Automatically revert buffers when they change on disk.  This doesn't apply to tramp.
(use-package autorevert
  :hook (after-init . global-auto-revert-mode)
  :custom
  (global-auto-revert-non-file-buffers t))

(use-package vc-hooks
  :custom
  (vc-follow-symlinks 'ask))

;; Minibuffer candidate completion
;; Display completion candidates vertically
(use-package vertico
  ;; :ensure t
  :hook (after-init . vertico-mode)
  :custom
  (vertico-resize nil) ;; don't shrink when there are less candidates
  ;; (vertico-group-format #("%s " 0 3 (face vertico-group-title))) ;; Don't put a vertical line across group titles
  )

;; Interact with vertico with the mouse.
(use-package vertico-mouse
  :after vertico
  :custom (vertico-mouse-mode t))

;; More comfortable directory navigation
(use-package vertico-directory
  :after vertico
  :bind (:map vertico-map
              ("RET" . vertico-directory-enter)
              ("<backspace>" . vertico-directory-delete-char)
              ("C-<backspace>" . vertico-directory-delete-word))
  ;; If i type ~/ etc in a find-file prompt, get rid of the preceding directory names for a cleaner look.
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

;; Prefix the current candidate with “» ”. From
;; https://github.com/minad/vertico/wiki#prefix-current-candidate-with-arrow
(advice-add #'vertico--format-candidate :around
            (lambda (orig cand prefix suffix index _start)
              (setq cand (funcall orig cand prefix suffix index _start))
              (concat
               (if (= vertico--index index)
                   (propertize "» " 'face 'vertico-current)
                 "  ")
               cand)))

;; Annotations for completing-read
(use-package marginalia
  ;; :ensure t
  :defer t
  :hook (after-init . marginalia-mode)
  ;; :custom
  ;; (marginalia-mode t)
  )


;; Basic matches candidates with the same text before the point, and the text after the point as a substring.
;; Orderless takes any number of space separated components and displays candidates that much every component in any order.
;; I don't actually know if these make any difference when i've specified a different completion style.
(use-package minibuffer
  :custom
  (read-file-name-completion-ignore-case t))

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

;; History

(use-package savehist
  :custom
  (savehist-file "~/.config/emacs/savehist")
  (savehist-additional-variables
        '(kill-ring
          command-history
          set-variable-value-history
          custom-variable-history
          query-replace-history
          read-expression-history
          minibuffer-history
          read-char-history
          face-name-history
          bookmark-history
          file-name-history))
  :hook (after-init . savehist-mode))

(use-package consult
  ;; :ensure t
  :defer t
  ;; :commands (consult-complete-in-region)
  :bind
  (
   ;; ([remap switch-to-buffer] . consult-buffer)
   ([remap yank-pop] . consult-yank-pop)
   ([remap goto-line] . consult-goto-line)
   ;; ([remap imenu] . consult-imenu)
   ;; ([remap info] . consult-info)
   ([remap repeat-complex-command] . consult-complex-command)
   ([remap bookmark-jump] . consult-bookmark))
  ;; :custom
  ;; Use consult for completion at point
  ;; (completion-in-region-function 'consult-completion-in-region)
  )



;; Some settings for nicer completion with the default emacs completion buffer.  I don't use this, because i use vertico.
;; (setopt completion-auto-help 'lazy
;;         completion-auto-select 'second-tab
;;         completion-show-help nil
;;         completions-sort nil
;;         completions-header-format nil)

;; Completion styles
;; When given a prompt to select from a list of candidates, there are quite a lot of things we can tweak to improve the experience.

;; Make leading whitespace a uniform width, so indentation still works with proportional fonts
(use-package elastic-indent
  :disabled t
  :hook (prog-mode . elastic-indent-mode))

;; Change the width of tab characters within lines to make subsequent lines form a table-like layout.
(use-package elastic-table
  :disabled t
  :hook (prog-mode-hook . elastic-table-mode))

;; Indenting lisp with tabs is horrible.  H/t acdw
(defvar space-indent-modes '(emacs-lisp-mode
                             lisp-interaction-mode
                             lisp-mode
                             scheme-mode
                             python-mode)
  "Modes to indent with spaces, not tabs.")
(add-hook 'prog-mode-hook
          (defun indent-tabs-mode-maybe ()
            (setq indent-tabs-mode
                  (if (apply #'derived-mode-p space-indent-modes) nil t))))

;; Make sentence navigation commands work for people who are wrong
;; NOTE: paragraphs does not provide itself
(use-package paragraphs
  :disabled t
  :custom
  (sentence-end-double-space nil))

;; If i write a script, i will always run chmod +x after saving it.  This command means i don't have to do that.
(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)

;; Zap up to char
(global-set-key (kbd "M-z") 'zap-up-to-char)

;; Insert date and time at point
;; Sometimes it's useful to be able to add a timestamp.  I use this for notes.  Use the same binding as notepad.exe.
(defun noa/insert-date (&optional time)
  "Insert the current date into the buffer.
With prefix argument TIME, also add the current time."
  (interactive "P")
  (if time
      (insert (format-time-string "[%Y-%m-%d %H:%M]"))
    (insert (format-time-string "[%Y-%m-%d]"))))
(defun noa/insert-datetime ()
  (interactive)
  (noa/insert-date t))

(global-set-key (kbd "<f5>") 'noa/insert-date-time)

;; Improve incremental search
(use-package isearch
  :bind (:map isearch-mode-map
              ("<return>" . noa/isearch-done-goto-start) ; always put point at the beginning of the match
              ("C-g" . isearch-cancel) ; always quit on C-g
              ("C-o" . isearch-occur))
  :init (defun noa/isearch-done-goto-start (&optional nopush edit)
          "End isearch at the start of the match."
          (interactive)
          (funcall #'isearch-done nopush edit)
          (when isearch-forward (goto-char isearch-other-end)))
  :custom
  (isearch-lax-whitespace t)
  (isearch-lazy-count t)
  (isearch-allow-motion t)
  (isearch-repeat-on-direction-change t)
  (isearch-wrap-pause 'no))

;; Make window management commands easier to press
(global-set-key (kbd "M-o") 'other-window)
(global-set-key (kbd "M-1") 'delete-other-windows)
(global-set-key (kbd "M-2") 'split-window-below)
(global-set-key (kbd "M-3") 'split-window-right)
(global-set-key (kbd "M-0") 'delete-window)

(global-set-key (kbd "C-x k") 'kill-current-buffer)

(defun snarf-song (url)
  (interactive "sYoutube url:")
  (async-shell-command
   (concat "yt-dlp -x --audio-format=mp3 -o "
           (shell-quote-argument "~/media/music/%(title)s [%(id)s].%(ext)s")
           " "
           (shell-quote-argument url))))


;;; Email

;; I keep coming back to rmail, despite its many, many warts.
;; Currently i fetch mail into Spool.mbox with fdm, then rmail moves it into Inbox.mbox.
(use-package rmail
  :commands (rmail)
  :custom
  (rmail-primary-inbox-list '("~/Documents/Library/Mail.new/Spool.mbox"))
  (rmail-file-name "~/Documents/Library/Mail.new/Inbox.mbox")
  (rmail-user-mail-address-regexp
   (rx "noa@noa.pub"))
  (rmail-mime-prefer-html nil)
  (rmail-mime-attachment-dirs-alist '(("" "~/media")))
  (rmail-displayed-headers
   (rx bol (or "To" "Cc" "From" "Date" "Subject") ":"))
  (rmail-delete-after-output t)
  (rmail-default-file "~/Documents/Library/Mail/Archive.mbox")
  (mail-dont-reply-to-names rmail-user-mail-address-regexp)

  (rmail-display-summary nil)
  (rmail-redisplay-summary t)
  (rmail-summary-line-count-flag nil)

  ;; :hook
  ;; ((rmail-show-message . visual-line-fill-column-mode))
  )

(use-package message
  :defer t
  :init
  (defun noa/message-default-headers ()
    (format "Fcc: ~/Documents/Library/Mail/outbox/%s.mbox"
            (format-time-string "%Y-%m")))

  :custom
  (message-default-headers #'noa/message-default-headers)
  (message-fill-column nil) ;; Don't enable auto-fill
  (user-full-name "noa")
  (user-mail-address "noa@noa.pub")
  (message-signature t)
  (message-signature-file "~/.config/signature")
  (message-send-mail-function 'message-send-mail-with-sendmail)
  (sendmail-program (executable-find "msmtpq"))
  (message-sendmail-extra-arguments '("--read-envelope-from"))
  (message-sendmail-envelope-from 'header)
  (message-kill-buffer-on-exit t)
  (message-sendmail-f-is-evil t)
  (message-forward-as-mime t)
  (message-interactive t)
  (message-auto-save-directory "~/Documents/Library/Mail/drafts"))

;; Environment variables
(setenv "PAGER" "cat")
(setenv "TERM" "dumb")
(setenv "NO_COLOR")
(setenv "GPG_AGENT_INFO" nil)

;; Dictionary
;; Emacs has built in support for interfacing with dictd.  With dictd and some dictionaries installed on debian, this works fine out of the box.
;; Unfortunately, dictionaries in this format tend to be hard wrapped and there isn't a lot of coverage outside of english
(use-package dictionary
  :defer t
  :custom
  (dictionary-search-interface nil))

;;; Bibliography management
;; TODO: update this to fit with the new single org file notes system.  This way should be easier than the previous method i think.
(use-package ebib
  ;; :ensure t
  :defer t
  :custom
  (ebib-bibtex-dialect 'biblatex)
  (ebib-index-columns '(("Author/Editor" 40 t)
                        ("Year" 6 t)
                        ("Title" 50 t)
                        ("Entry Key" 40 t)))
  ;; Put notes in my markdown notes directory.  I still need to find a way to make ebib-notes-name-transform-function return the appropriate date-based filename format.
  (ebib-notes-storage 'one-file-per-note)
  (ebib-notes-directory "~/Documents/Notes/References")
  (ebib-notes-show-note-method 'all)
  (ebib-notes-file-extension "md")
  (ebib-notes-template "# %T\n\n%%?")
  (ebib-file-associations '(("pdf" . "xdg-open") ("ps" . "gv"))))

(use-package citar
  ;; :ensure t
  :defer t
  :commands (citar-insert-citation citar-open-files citar-open-notes)
  :custom
  (citar-bibliography '("~/Documents/Library/References.bib"))
  (citar-library-paths '("~/Documents/Library/Papers"))
  (citar-notes-paths '("~/Documents/Notes"))
  )
(use-package citar-org
  :after oc
  :custom
  (org-cite-insert-processor 'citar)
  (org-cite-follow-processor 'citar)
  (org-cite-activate-processor 'citar))
  

(use-package bibtex
  :defer t
  :custom
  (bibtex-dialect 'biblatex)
  (bibtex-entry-format '(opts-or-alts
                         page-dashes
                         required-fields
                         numerical-fields
                         whitespace
                         last-comma
                         delimiters
                         unify-case
                         sort-fields))
  (bibtex-field-delimiters 'braces)
  (bibtex-entry-delimiters 'braces)
  ;; Use one name, appending ETAL if there is more than one author
  ;; The name should be all caps
  (bibtex-autokey-names 1)
  (bibtex-autokey-name-case-convert-function 'upcase)
  (bibtex-autokey-additional-names "ETAL")
  ;; Follow this with the full year
  ;; Probably don't need more than two digits actually
  (bibtex-autokey-name-year-separator "-")
  (bibtex-autokey-year-length 4)
  ;; Follow this with initials of the title
  (bibtex-autokey-year-title-separator "-")
  (bibtex-autokey-titlewords t)
  (bibtex-autokey-titleword-length -1) ;; One character
  (bibtex-autokey-titleword-separator "")
  (bibtex-autokey-titleword-case-convert-function 'upcase))

(use-package biblio
  :disabled t
  :ensure t
  :defer t
  :custom
  (biblio-bibtex-use-autokey t)
  (biblio-download-directory "~/Documents/Library/Papers"))


(defun noa/yank-buffer-file-name ()
  (interactive)
  (when (buffer-file-name)
    (kill-new (file-name-nondirectory (buffer-file-name)))))

(use-package org
  :defer t
  :init
  ;; Repurpose underline syntax for cloze deletion face
  (defface my-cloze '((t . (:box t))) "Cloze face for Inline-Anki")
  (setq org-emphasis-alist '(("*" bold)
                           ("/" italic)
                           ("_" my-cloze) ;; new
                           ("=" org-verbatim verbatim)
                           ("~" org-code verbatim)
                           ("+" (:strike-through t))))

  :custom
  (org-agenda-files '("~/Documents/Notes.org" "~/Documents/Notes/"))
  (org-replace-disputed-keys t)
  (org-support-shift-select 'always) ;; Still refuses to fix on timestamps :/
  (org-log-done 'time)
  (org-pretty-entities t)
  (org-startup-with-inline-images t)
  (org-image-actual-width '(300))
  (org-ellipsis " [+]")
  (org-insert-heading-respect-content t)
  (org-fontify-done-headline nil)
  (org-fontify-todo-headline nil)
  (org-fontify-whole-heading-line t)
  (org-src-fontify-natively t)
  (org-src-tab-acts-natively t)
  (org-edit-src-content-indentation 0)
  (org-use-sub-superscripts '{})

  :hook ((org-mode . font-lock-mode))

  )

(add-hook 'text-mode-hook (lambda ()
                            (setq-local line-spacing 0.1)))

(use-package oc
  :after org
  :custom
  (org-cite-global-bibliography '("~/Documents/Library/References.bib"))
  (org-cite-export-processors '((beamer csl)
                                (latex csl)
                                (t csl))))
(use-package oc-csl
  :after oc
  :custom
  (org-cite-csl-styles-dir "~/Documents/Library/CSL/Styles")
  (org-cite-csl-locales-dir "~/Documents/Library/CSL/Locales"))


;; Not working how i want
(use-package org-capture
  :bind ("C-c c" . org-capture)
  :custom
  (org-capture-templates
        '(("d" "Today's diary" entry
           (file+headline "~/Documents/Notes.org" "Diary")
           "** %u\n%?"))))

;; See https://llazarek.github.io/blog/2018/07/organization-with-org-mode.html
;; for configuring the org agenda

(use-package inline-anki
  :defer t
  :after org
  :load-path "~/.config/emacs/site-lisp/inline-anki"
  :config
  (add-to-list 'org-structure-template-alist '("f" . "flashcard")))

(use-package ctrlj
  :load-path "~/Documents/Projects/2024-10-08 Ctrlj"
  :init (global-unset-key (kbd "C-j"))
  :bind* (("<f2>" . ctrlj/open-note)
         ("C-j C-j" . ctrlj/open-note)
;;         ("C-j C-n" . ctrlj/new-note)
         ("C-j C-l" . ctrlj/insert-link-to-note)
         ("C-j C-d" . ctrlj/open-todays-diary)
         ("C-j C-a" . ctrlj/view-agenda)
         ("C-j C-o" . ctrlj/jump-to-other-related-note)))

;; Experimental website generator
(use-package ox-hugo
  :disabled t
  :ensure t
  :after ox)

(defun noa/consult-rg-notes ()
  (interactive)
  (consult-ripgrep "~/Documents/Notes"))
(global-set-key (kbd "C-j C-s") #'noa/consult-rg-notes)

(use-package emms
  :disabled t
 :config
 (require 'emms-setup)
 (require 'emms-info-metaflac)
 (require 'emms-info-mp3info)
 (require 'emms-info-ogginfo)
 (emms-all)
 (emms-default-players)
 (setq emms-source-file-default-directory "~/Music/")
 (setq emms-browser-covers 'emms-browser-cache-thumbnail-async)
 (setq emms-playlist-default-major-mode 'emms-playlist-mode)
 (setq emms-track-description-function 'emacs-init/emms-track-description)
 (add-to-list 'emms-info-functions 'emms-info-libtag)
 (add-to-list 'emms-info-functions 'emms-info-mp3info)
 (add-to-list 'emms-info-functions 'emms-info-ogginfo)
 (global-set-key (kbd "C-c e p") 'emms-playlist-mode-go)
 (global-set-key (kbd "C-c e r") 'emms-toggle-repeat-playlist)
 (global-set-key (kbd "C-c e s") 'emms-toggle-random-playlist))

;; Cleaner modeline
;; Worth switching to this one if it ever ends up on melpa:
;; https://github.com/lambda-emacs/lambda-line
(use-package mood-line
  :disabled t
  :ensure t
  :custom
  (mood-line-mode t))

;; Experiment with embark
(use-package embark
  ;; :ensure t

  :bind
  (("C-<return>" . embark-act)
   ;; ("M-<return>" . embark-dwim)
   ("C-h B" . embark-bindings)) ;; alternative for `describe-bindings'

  :init
  ;; Optionally replace the key help with a completing-read interface
  (setq prefix-help-command #'embark-prefix-help-command))

(use-package embark-consult
  :disabled t
  :after (embark consult)
  :ensure t
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

(use-package which-key
  :disabled t
  :custom
  (which-key-mode t))


(use-package pdf-tools
  :when (display-graphic-p)
  ;; :ensure t
  :magic ("%PDF" . pdf-view-mode)
  :config
  (pdf-tools-install :no-query))

;; Common lisp development

(use-package sly
  :disabled t
  :if (executable-find "sbcl")
  :ensure t
  :commands 'sly
  :custom ((inferior-lisp-program (executable-find "sbcl"))
           (common-lisp-hyperspec-root "file:///usr/share/doc/hyperspec/"))
  :mode ((rx (or ".lisp" ".cl") eos) . common-lisp-mode))

;; Html

(use-package web-mode
  ;; :ensure t
  :mode "\\.html?\\'"
  :mode "\\.css\\'"
  :mode "\\.phtml\\'"
  :mode "\\.tpl\\.php\\'"
  :mode "\\.[agj]sp\\'"
  :mode "\\.as[cp]x\\'"
  :mode "\\.erb\\'"
  :mode "\\.mustache\\'"
  :mode "\\.djhtml\\'"
  :custom
  (web-mode-markup-indent-offser 4)
  (web-mode-css-indent-offset 4)
  (web-mode-code-indent-offset 4))

;; Write url to text file
(defvar noa/record-url-file "~/Documents/Library/urls")
(defun noa/record-url (url &rest args)
  "An alternative browser function that appends the url to a file.  We do not pay attention to any arguments."
  (write-region (concat url "\n") nil noa/record-url-file 'append))

(defvar note/directory "~/Documents/Notes"
  "The directory in which notes are stored.")
(defvar note/file-ending "md"
  "The file ending for new notes.")

(defun note/new (&optional other-window)
  (interactive "P")
  (let* ((uid (format-time-string "%Y-%m-%d-%H%M%S"))
         (note (concat note/directory "/" uid "." note/file-ending)))
    (if other-window
        (progn
          (insert "[[./" uid ".org]]")
          (find-file-other-window note))
      (find-file note))
    ))

(use-package ediff
  :defer t
  :custom
  (ediff-window-setup-function 'ediff-setup-windows-plain)
  (ediff-split-window-function 'split-window-horizontally))

(use-package proced
  :defer t
  :custom
  (proced-auto-update-flag t)
  (proced-auto-update-interval 1)
  (proced-enable-color-flag nil)
  (proced-show-remote-processes t))

;; (profiler-stop)