🏡 index : yaqubmacs.git

#+PROPERTY: header-args :tangle "/home/yaqub/.emacs.d/init.el"

* Initial Setup

** Niceties

#+begin_src emacs-lisp

  (setq ring-bell-function 'ignore)

  (setq inhibit-startup-message t)
  (setq initial-buffer-choice t)

#+end_src

** Backup to saves instead of leaving temporary files in .

#+begin_src emacs-lisp
  (setq backup-directory-alist `(("." . "~/.saves")))
  (setq backup-by-copying t)
#+end_src

** Initialise use-package

#+begin_src emacs-lisp
  (require 'package)
  (setq package-archives '(("melpa" . "https://melpa.org/packages/")
                           ("org" . "https://orgmode.org/elpa/")
                           ("gnu" . "https://elpa.gnu.org/packages/")))
  (package-initialize)
  ;; Bootstrap use-package if not installed
  (unless (package-installed-p 'use-package)
    (package-refresh-contents)
    (package-install 'use-package))

  ;; Ensure packages are installed automatically
  (setq use-package-always-ensure t)

#+end_src

* Appearance
** Fonts

#+begin_src emacs-lisp
  ;; Set the default font for Latin, Greek, and Cyrillic
  (set-face-attribute 'default nil
                      :family "SF Mono Terminal Regular"
                      :height 160)  ;; Adjust height as needed

  (set-face-attribute 'fixed-pitch nil
                      :family (face-attribute 'default :family)
                      :height (face-attribute 'default :height)
                      :weight (face-attribute 'default :weight))

  (set-face-attribute 'fixed-pitch-serif nil
                      :family (face-attribute 'default :family)
                      :height (face-attribute 'default :height)
                      :weight (face-attribute 'default :weight))

  ;; Set the font for CJK characters to Source Han Serif with double width
  (dolist (charset '(kana han cjk-misc bopomofo))
    (set-fontset-font t charset
                      (font-spec :family "Source Han Serif" :height 0.85)
                      nil 'append))
  ;; Fallback to Andale Mono for any Unicode characters not covered by the above
  (set-fontset-font t 'unicode
                    (font-spec :family "Andale Mono")
                    nil 'append)

#+end_src


** Mac env. specific settings
#+begin_src emacs-lisp
  (menu-bar-mode 1)
  (toggle-scroll-bar -1)
  (tool-bar-mode -1)

  (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
  (add-to-list 'default-frame-alist '(ns-appearance . dark))


#+end_src

** Theming and modeline

#+begin_src emacs-lisp
  (use-package bliss-theme
    :ensure t
    :config
    (load-theme 'bliss t))

  (use-package minions
    :ensure t
    :config
    (setq minions-mode-line-lighter "多")
    (setq minions-mode-line-delimiters '("" . ""))
    (minions-mode 1))
#+end_src

* QoL Packages

#+begin_src emacs-lisp
  (use-package bind-key
    :ensure t)

  (use-package olivetti
    :ensure t)

  (use-package vertico
    :ensure t
    :init
    (vertico-mode)
    (setq vertico-cycle t))  ;; Cycle through options like ivy

  ;; Vertico for better minibuffer completion (replaces ivy)
  (use-package vertico
    :ensure t
    :init
    (vertico-mode)
    (setq vertico-cycle t))  ;; Cycle through options like ivy

  ;; Marginalia for annotations (docstrings, keybindings, etc.)
  (use-package marginalia
    :ensure t
    :init
    (marginalia-mode))

  ;; Consult for enhanced M-x, buffer switching, and more (replaces counsel)
  (use-package consult
    :ensure t
    :demand t
    :bind
    (("C-s" . consult-line)                  ;; Replaces swiper
     ("C-x b" . consult-buffer)              ;; Replaces counsel-switch-buffer
     ("C-M-j" . consult-buffer)              ;; Buffer switch (ivy-like behavior)
     :map minibuffer-local-map
     ("C-r" . consult-history)))             ;; Replaces counsel-minibuffer-history

  ;; Orderless for fuzzy matching (replaces ivy's fuzzy searching)
  (use-package orderless
    :ensure t
    :init
    (setq completion-styles '(orderless basic)
          orderless-matching-styles '(orderless-literal
                                      orderless-regexp
                                      orderless-flex)))

  ;; Evil Mode (Vi-like keybindings)
  (use-package evil
    :ensure t
    :init
    (setq evil-want-integration t
          evil-want-keybinding nil)
    (evil-mode 1))

  ;; Evil-collection for better integration with Emacs commands
  (use-package evil-collection
    :ensure t
    :after evil
    :config
    (evil-collection-init))

  (use-package evil-org
    :ensure t
    :after org
    :hook (org-mode . (lambda () evil-org-mode))
    :config
    (require 'evil-org-agenda)
    (evil-org-agenda-set-keys))

  ;; Replace buffer switcher with consult-buffer in Evil ex-mode
  (evil-ex-define-cmd "b" 'consult-buffer)

  ;; Which-key for displaying available keybindings
  (use-package which-key
    :ensure t
    :init
    (which-key-mode))

  ;; Persist minibuffer history
  (use-package savehist
    :ensure t
    :init
    (savehist-mode))

  ;; Embark for acting on minibuffer selections
  (use-package embark
    :ensure t
    :bind
    (("C-." . embark-act)  ;; Trigger actions on selected candidates
    )
    :init
    (setq embark-prompter 'embark-keymap-prompter))

  ;; Embark-consult for seamless integration
  (use-package embark-consult
    :ensure t
    :after (embark consult)
    :hook
    (embark-collect-mode . consult-preview-at-point-mode))
  (add-hook 'prog-mode-hook 'display-line-numbers-mode)

  ;; Define a function to clean up whitespace in the buffer
  (defun yaqub/cleanup-buffer-whitespace ()
    "Clean up whitespace in the buffer, including converting tabs to spaces."
    (interactive)
    (whitespace-cleanup))

  ;; Define a function to set the key binding in programming modes
  (defun yaqub/set-cleanup-whitespace-key ()
    (local-set-key (kbd "C-c w") 'yaqub/cleanup-buffer-whitespace))

  ;; Add the key binding function to the prog-mode-hook
  (add-hook 'prog-mode-hook 'yaqub/set-cleanup-whitespace-key)
#+end_src
** Modeline appearance
#+begin_src emacs-lisp
  ;; Set all modeline text to orange
  (set-face-attribute 'mode-line nil
                      :foreground "orange"
                      :background (face-attribute 'mode-line :background)
                      :weight 'normal)

  (set-face-attribute 'mode-line-inactive nil
                      :foreground "orange"
                      :background (face-attribute 'mode-line-inactive :background)
                      :weight 'normal)

  ;; Set modeline buffer ID to Microgramma
  (set-face-attribute 'mode-line-buffer-id nil
                      :family "MicrogrammaDMedExt"
                      :weight 'normal)
#+end_src

#+RESULTS:
** Evil undo tree
#+begin_src emacs-lisp
  (use-package undo-tree
    :ensure t
    :config
    (global-undo-tree-mode)
    (evil-set-undo-system 'undo-tree))
#+end_src

* Coding
** Enable line numbers in prog-mode
#+begin_src emacs-lisp
  (add-hook 'prog-mode-hook 'display-line-numbers-mode)
#+end_src
** Enable whitespace cleanup in prog-mode
#+begin_src emacs-lisp
    ;; Define a function to clean up whitespace in the buffer
    (defun yaqub/cleanup-buffer-whitespace ()
      "Clean up whitespace in the buffer, including converting tabs to spaces."
      (interactive)
      (whitespace-cleanup))

    ;; Define a function to set the key binding in programming modes
    (defun yaqub/set-cleanup-whitespace-key ()
      (local-set-key (kbd "C-c w") 'yaqub/cleanup-buffer-whitespace))

    ;; Add the key binding function to the prog-mode-hook
    (add-hook 'prog-mode-hook 'yaqub/set-cleanup-whitespace-key)
#+end_src
** Setup Yasnippet
Yasnippet is a template system for Emacs.

#+begin_src emacs-lisp
  (use-package yasnippet
    :ensure t
    :init
    (yas-global-mode 1))
#+end_src

** Enable dired-sidebar

#+begin_src emacs-lisp
  (use-package dired-sidebar
    :bind (("C-x C-n" . dired-sidebar-toggle-sidebar))
    :ensure t
    :commands (dired-sidebar-toggle-sidebar)
    :init
    (add-hook 'dired-sidebar-mode-hook
              (lambda ()
                (unless (file-remote-p default-directory)
                  (auto-revert-mode))))
    :config
    (push 'toggle-window-split dired-sidebar-toggle-hidden-commands)
    (push 'rotate-windows dired-sidebar-toggle-hidden-commands)

    (setq dired-sidebar-subtree-line-prefix "__")
    (setq dired-sidebar-theme 'vscode)
    (setq dired-sidebar-use-term-integration t)
      (setq dired-sidebar-use-custom-font t))
#+end_src
** Setup Company-mode
Company mode is a text completion framework.

#+begin_src emacs-lisp
  (use-package company
    :ensure t
    :init
    (global-company-mode))
#+end_src

*** Setup Company-box
Company-box is a frontend for company-mode, it provides a dropdown menu interface for completions.

#+begin_src emacs-lisp
  (use-package company-box
    :ensure t
    :hook (company-mode . company-box-mode))
#+end_src

** Setup Editorconfig

#+begin_src emacs-lisp
  (use-package editorconfig
    :ensure t
    :config
    (editorconfig-mode 1))
#+end_src
** Magit
Magit is an interface to the version control system Git, implemented as an Emacs package. It aspires to be a complete Git porcelain.

#+begin_src emacs-lisp
  (use-package magit
    :ensure t
    :bind (("C-x g" . magit-status)))
#+end_src
** Auto-install treesit grammars
#+begin_src emacs-lisp
  (use-package treesit-auto
    :ensure t
    :config
    (setq treesit-auto-install 'prompt) ; Automatically install grammars
    (global-treesit-auto-mode))
#+end_src

** Smart Parens

#+begin_src emacs-lisp
  (use-package smartparens
    :ensure smartparens  ;; install the package
    :hook (prog-mode text-mode markdown-mode) ;; add `smartparens-mode` to these hooks
    :config
    ;; load default config
    (require 'smartparens-config))  
#+end_src

* Writing
** Global bibliography variables
#+begin_src emacs-lisp
  (setq yaqub/global-bibliography '("~/Zotero/References.bib")
        yaqub/global-library-path '("~/Zotero")
        yaqub/global-notes-path "~/Orgfiles/roam/")
#+end_src
** Setup Org
Here, we separate the org setup into its own function for clarity.

#+begin_src emacs-lisp
  (use-package org
    :ensure t
    :custom
    (org-hide-emphasis-markers t)
    (org-startup-indented t)
    (org-startup-with-inline-images t)
    :config
    ;; Enable olivetti on all org buffers
    (add-hook 'org-mode-hook 'olivetti-mode)
    ;; add keybindings for org-mark-ring-goto at C-c C-<left> and C-c C-<right>
    (define-key org-mode-map (kbd "C-c <left>") 'org-mark-ring-goto)
    (define-key org-mode-map (kbd "C-c <right>") 'org-mark-ring-goto)
    ;; add keybindings for org-mark-ring-push at C-c C-<up> and C-c C-<down>
    (define-key org-mode-map (kbd "C-c <up>") 'org-mark-ring-push)
    (define-key org-mode-map (kbd "C-c <down>") 'org-mark-ring-push)
    )
#+end_src

*** Org-roam
Very important.
#+begin_src emacs-lisp
  (use-package org-roam
    :ensure t
    :custom
    (org-roam-directory "~/Orgfiles/roam")
    :bind (("C-c n f" . org-roam-node-find)
           ("C-c n i" . org-roam-node-insert)
           ("C-c n g" . org-roam-graph)
           ("C-c n c" . org-roam-capture)
           ("C-c n d" . org-roam-dailies-goto-today)
           ("C-c n D" . org-roam-dailies-goto-date)))
#+end_src
*** Making sure org links open in the same window
#+begin_src emacs-lisp
    (setq org-link-frame-setup '((file . find-file)))
#+end_src
When org-roam is enabled, make org-attach always attach to the /file/, not the /heading/.

#+begin_src emacs-lisp
  (defun yaqub/org-attach-use-file-id ()
    "Use the top-level :ID: property for attachments unless overridden by :ATTACH_INDIVIDUALLY:."
    (save-excursion
      (goto-char (point-min))
      (let ((file-id (org-entry-get (point) "ID" t))
            (attach-individual (org-entry-get (point-min) "ATTACH_INDIVIDUALLY")))
        (if (and file-id (not attach-individual))
            (setq-local org-attach-id-dir (concat (file-name-as-directory org-attach-directory) file-id))))))

  (add-hook 'org-mode-hook 'yaqub/org-attach-use-file-id)

  (defun yaqub/org-attach-individually ()
    "Add :ATTACH_INDIVIDUALLY: property to the current heading."
    (interactive)
    (org-set-property "ATTACH_INDIVIDUALLY" "t")
    (message ":ATTACH_INDIVIDUALLY: property added."))

  (defun yaqub/org-attach-file-to-file-id ()
    "Attach a file to the top-level ID if present, regardless of cursor position."
    (interactive)
    (save-excursion
      (goto-char (point-min))
      (let ((file-id (org-entry-get (point) "ID" t)))
        (if file-id
            (setq-local org-attach-id-dir (concat (file-name-as-directory org-attach-directory) file-id)))))
    (save-excursion
      (goto-char (point-min))
      (org-attach)))

  ;; Bind this to the usual attachment keybinding (C-c c-a)
  (with-eval-after-load 'org
    (define-key org-mode-map (kbd "C-c c-a") #'yaqub/org-attach-file-to-file-id))
#+end_src

** Setup LaTeX
*** Set up AucTeX
#+begin_src emacs-lisp
  (use-package auctex
    :ensure t
    :mode ("\\.tex\\'" . LaTeX-mode)
    :config
    (setq TeX-auto-save t)
    (setq TeX-parse-self t)
    (setq-default TeX-master nil)

    (add-hook 'LaTeX-mode-hook 'visual-line-mode)
    (add-hook 'LaTeX-mode-hook 'flyspell-mode)
    (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)

    (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
    (setq reftex-plug-into-AUCTeX t)
    (setq TeX-PDF-mode t)

    ;; Use Biber with AUCTeX
    (setq TeX-command-BibTeX "Biber"))
#+end_src
* Reading
** Setup elfeed
*** Elfeed
#+begin_src emacs-lisp
  (use-package elfeed
    :ensure t)
#+end_src
*** TODO Elfeed Org
*** TODO Elfeed Youtube
* Anki Integration
#+begin_src emacs-lisp
  (use-package anki-editor
    :ensure t
    :config
    (global-set-key (kbd "C-c a a") 'anki-editor-insert-note)
    (global-set-key (kbd "C-c a c") 'anki-editor-cloze-region)
    (global-set-key (kbd "C-c a p") 'anki-editor-push-notes))
#+end_src

* Mentor Torrent Client
#+begin_src emacs-lisp
  (use-package mentor
    :ensure t
    :after evil
    :config
    (setq mentor-rtorrent-external-rpc "~/.rtorrent-rpc.socket")
    (evil-set-initial-state 'mentor-mode 'emacs))
#+end_src

* Minibuffer Shortcuts
#+begin_src emacs-lisp
  ;; the command "eval-init" evaluates init.el
  (defun eval-init ()
    (interactive)
    (load-file "~/.emacs.d/init.el"))

  ;; the command "home" opens home orgfile
  (defun home ()
    (interactive)
    (find-file "/media/storage/Orgfiles/roam/home.org"))

  ;; the command "todo" opens todo orgfile
  (defun todo ()
    (interactive)
    (find-file "/media/storage/Orgfiles/roam/todo.org"))

  ;; the command "yaqubmacs" opens dired at ~/Development/yaqubmacs
  (defun yaqubmacs ()
    (interactive)
    (dired "/media/storage/Development/yaqubmacs"))
#+END_SRC
* Safe local vars
Unsafe, but convenient
#+begin_src emacs-lisp
  (setq enable-local-variables :all)
#+end_src