Skip to content

Latest commit

 

History

History
1726 lines (1361 loc) · 47 KB

config.org

File metadata and controls

1726 lines (1361 loc) · 47 KB

The Emacs Operating System

Table of contents

Overview

Why do I like Emacs, you ask? For me, it’s like riding a sports bike; it’s not for everyone, but it’s exhilarating! It has its quirks and might not be the most efficient tool for the job, but it’s a joy to use. As a “professional” programmer, I should probably use the most efficient tool for the job, but as long as my employers can’t tell the difference, I’ll stick with Emacs.

https://imgs.xkcd.com/comics/real_programmers.png

Initialization

Faster Startup

Increasing the garbage collection threshold during startup can reduce time spent in GC, speeding things up:

;; Increase to 50MB during startup
(setq gc-cons-threshold (* 50 1000 1000))
(add-hook 'emacs-startup-hook
          ;; Restore to 2MB after startup
          (lambda () (setq gc-cons-threshold (* 2 1000 1000))))

Custom Variables

Directories

Consolidate all the directories here.

;; Define custom variables for directories
(defvar eos/org-images-dir "~/Documents/org/images"
  "Directory to store images downloaded with org-download.")

(defvar eos/org-roam-dir "~/Documents/org/hivemind/notes"
  "Directory to store org-roam notes.")

(defvar eos/emacs-backup-dir "~/.emacs.d/emacs-backup"
  "Directory where Emacs backup files are stored.")

;; Don't create these directories if not present
(defvar eos/dev-dir "~/dev"
  "Directory for personal development projects.")

(defvar eos/work-dir "~/work"
  "Directory for work projects.")

(defvar eos/school-dir "~/school"
  "Directory for school projects.")

(defvar eos/project-dirs
  (list eos/dev-dir eos/work-dir eos/school-dir)
  "List of all directories for personal, work, and school projects.")

(defvar eos/music-dir "~/Documents/Music"
  "Directory for local music files.")

;; Function to ensure a directory exists
(defun eos/ensure-directory-exists (dir)
  "Ensure that the directory DIR exists. Create it if necessary."
  (unless (file-exists-p dir)
    (make-directory dir t)))

;; Ensure required directories exist
(mapc #'eos/ensure-directory-exists
      (list eos/org-images-dir
            eos/org-roam-dir
            eos/emacs-backup-dir))

Basics

Basic setup for Emacs appearance and behavior.

  • Disable toolbars, scrollbars, and tooltips.
    (scroll-bar-mode -1)
    (tool-bar-mode -1)
    (tooltip-mode -1)
    (set-fringe-mode 0)
    (menu-bar-mode -1)
        
  • Set scroll length
    (setq scroll-step 1 scroll-conservatively 10000)
        
  • Set visible bell
    (setq visible-bell t)
        
  • Set fonts. TODO: Check if the font exists
    ;; (set-face-attribute 'default nil :font "Cascadia Code NF" :height 160)
    (set-face-attribute 'default nil :font "Iosevka" :height 160)
        
  • Configure line numbers
    ;;(global-display-line-numbers-mode t)
    
    ;; We don't want line numbers in certain modes, e.g., shell and pdf-view
    ;; (dolist (mode '(org-mode-hook
    ;;                 term-mode-hook
    ;;                 eshell-mode-hook
    ;;                 shell-mode-hook
    ;;                 pdf-view-mode-hook))
    ;;   (add-hook mode (lambda () (display-line-numbers-mode 0))))
    
    (add-hook 'prog-mode-hook 'display-line-numbers-mode)
        
  • Configure tabs behavior
    ;; (tab-bar-mode 1)
    (setq-default indent-tabs-mode nil)
    (setq-default tab-width 4)
    (setq indent-line-function 'insert-tab)
        
  • Display vertical line at 79 characters
    (setq-default display-fill-column-indicator-column 79)
    (add-hook 'prog-mode-hook 'display-fill-column-indicator-mode)
        
  • Customize window behavior.
    ;; Start emacs in fullscreen mode (Mac tiling manager conflict)
    ;; (add-to-list 'initial-frame-alist '(fullscreen . maximized))
    ;; (add-to-list 'default-frame-alist '(fullscreen . maximized))
    
    ;; Remove title bar with maximize and minimize options
    ;; (add-to-list 'default-frame-alist '(undecorated . t))
    ;; (add-to-list 'default-frame-alist '(drag-internal-border . 1))
    
    ;; Add an empty header if title bars are removed
    ;; (setq-default header-line-format " ")
    
    
    ;; This displays "Emacs - buffer_name"
    ;; (setq frame-title-format
    ;;       '("Emacs - %b"))  
    ;; (add-to-list 'default-frame-alist
    ;;              '(title . "Emacs"))
    
    (setq frame-title-format "I see dead people.\n")
    (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
    (add-to-list 'default-frame-alist '(ns-appearance . dark))
    (setq ns-use-proxy-icon nil)
    
        
  • Backup in one place, flat, no tree structure
    (setq backup-directory-alist `((".*" . ,eos/emacs-backup-dir)))
        
  • MacOs smooth scrolling
    (setq scroll-conservatively 101)
    (setq mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control) . nil)))
    (setq mouse-wheel-progressive-speed nil)
        
  • Miscellaneous
    ;; Truncate long lines in certain modes
    (add-hook 'org-mode-hook (lambda() (setq truncate-lines nil)))
    
        

Package Management

Setting up package managers and installing essential packages.

Intialize packages

  • Initialize Melpa
(require 'package)
(setq package-archives '(("elpa"   . "https://elpa.gnu.org/packages/")
                         ("melpa"  . "https://melpa.org/packages/")
                         ("melpa-stable" . "https://stable.melpa.org/packages/")
                         ("org" . "https://orgmode.org/elpa/")))

(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

;; Initialize use-package on non-linux platforms
(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)

Install straight.el

use-package does not support the additional properties like :type, :host, :repo, etc.). Instead, We want tose the straight.el package manager to handle packages from non-standard sources like Git repositories.

(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)

Avoid Org version mismatch with Straight.el

(straight-use-package '(org :type built-in))

Appearence

All the Icons

Why we need all the icons? Because they are pretty.

(use-package all-the-icons
  :ensure t)

Themes

Doom Emacs has consolidated a bunch of nice looking themes, we would like to get them all.

(use-package doom-themes
  :straight (:host github :repo "doomemacs/themes")
  :ensure t
  :config
  ;; Global settings (defaults)
  (setq 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)
  ;; Enable custom neotree theme (all-the-icons must be installed!)
  (doom-themes-neotree-config)
  ;; or for treemacs users
  ;; (setq doom-themes-treemacs-theme "doom-gruvbox")
  (doom-themes-treemacs-config)
  ;; Corrects (and improves) org-mode's native fontification.
  (doom-themes-org-config))

(load-theme 'leuven t)

I love how Org files look with poet theme, so it’s a must.

(use-package poet-theme
  :ensure t)

Auto-Dark mode

For the past 6 months, I have been manually switching between light and dark themes, until I found this package.

(setq auto-dark-allow-osascript t)

(use-package auto-dark
  :ensure t
  :straight t
  :custom
  (auto-dark-themes '((wombat) (leuven)))
  (auto-dark-polling-interval-seconds 5)
  (auto-dark-allow-osascript nil)
  (auto-dark-allow-powershell nil)
  ;; (auto-dark-detection-method nil) ;; dangerous to be set manually
  :hook
  (auto-dark-dark-mode
   . (lambda ()
        ;; something to execute when dark mode is detected
        ))
  (auto-dark-light-mode
   . (lambda ()
        ;; something to execute when light mode is detected
        ))
  :init (auto-dark-mode))

Better Focus

An asthetic plugin designed to visually distinguish “real” buffers from “unreal” buffers (like popups, sidebars, log-buffers, etc) by giving the later a slightly different background.

(use-package solaire-mode
  :straight t
  :config
  (solaire-global-mode +1))

Dimmer mode indicates which buffer is currently active by dimming the faces in the other buffers.

(use-package dimmer
  :straight t
  :config
  (dimmer-configure-which-key)
  (dimmer-mode t))

(setq dimmer-fraction .3)

Transparency

Toggle transparency for fun and no profit. Emacs on MacOS doesn’t support transparency, However it’s still fun to have it when using a tiling window manager on a large monitor.

(defvar transparency-level-active 85
  "Opacity level when Emacs is active.")

(defvar transparency-level-inactive 85
  "Opacity level when Emacs is inactive.")

(defvar transparency-enabled t
  "Toggle for the transparency feature.")

Dashboard

We would need emacs-dashboard package for an easier configuration.

(use-package dashboard
  :config
  (setq dashboard-center-content t)
  (setq dashboard-show-shortcuts nil)
  :init
  (dashboard-setup-startup-hook))

Modeline

Nano Modeline

A minimal modeline for Emacs.

;; Hide the default mode line globally
;; (setq-default mode-line-format nil)

;; Set the nano-modeline position to bottom before loading
;; nano-modeline.
;; (setq nano-modeline-position 'nano-modeline-footer)

;; Install nano-modeline
;; (use-package nano-modeline
;;   :ensure t
;;   :hook
;;   (prog-mode-hook . nano-modeline-prog-mode)
;;   (text-mode-hook . nano-modeline-text-mode)
;;   (org-mode-hook  . nano-modeline-org-mode)
;;   (term-mode-hook . nano-modeline-term-mode)
;;   :init (nano-modeline-prog-mode t))
Doom Modeline

I keep getting bored with “nicer” looking modelines and keep coming back to the default Emacs one. Here are a few tweaks to make it look good.

(use-package doom-modeline
  :straight t
  :init (doom-modeline-mode 1))

Fancy Mini-Buffer

I like a floating minibuffer, but ivy-posframe] looks better. Mini-frame mode is enabled by default.

(use-package mini-frame
  :straight t
  :config
  (mini-frame-mode 1))

;; make sure they are in the middle of the screen
(custom-set-variables
 '(mini-frame-show-parameters
   '((top . 200)
     (width . 0.7)
     (left . 0.5))))

Indentation

Indent bars is a better package, however each bar is flanked by two dotted line and looks quite weird.

GitHub issue: jdtsmith/indent-bars#84

;; (use-package indent-bars
;; :hook ((prog-mode) . indent-bars-mode))

highlight-indent-guides

(use-package highlight-indent-guides
  :ensure t
  :straight t
  :config (setq highlight-indent-guides-method 'character)
  :hook (prog-mode . highlight-indent-guides-mode))

Functionality

Evil Mode

  • Since I have been using VI for quite sometime now, I don’t want to train myself to learn Emacs
    (use-package evil
      :init
      (setq evil-want-integration t)
      (setq evil-want-keybinding nil)
      (setq evil-want-C-u-scroll t)
      (setq evil-want-C-i-jump nil)
      :config
      (evil-mode 1)
      (define-key evil-insert-state-map (kbd "C-g") 'evil-normal-state)
      (define-key evil-insert-state-map (kbd "C-h") 'evil-delete-backward-char-and-join))
    
    ;; Unbind certain keys
    (with-eval-after-load 'evil-maps
      (define-key evil-motion-state-map (kbd "SPC") nil)
      (define-key evil-motion-state-map (kbd "RET") nil)
      (define-key evil-motion-state-map (kbd "TAB") nil))
    
    ;; Use visual line motions even outside of visual-line-mode buffers
    (evil-global-set-key 'motion "j" 'evil-next-visual-line)
    (evil-global-set-key 'motion "k" 'evil-previous-visual-line)
    
    (evil-set-initial-state 'messages-buffer-mode 'normal)
    (evil-set-initial-state 'dashboard-mode 'normal)
        
  • Evil on every mode
    ;; package: evil-collection
    ;; Now be EVIL on every mode
    ;; TODO: Doesn't work
    (use-package evil-collection
      :after evil
      :ensure t
      :config
      (evil-collection-init))
        

Org Mode

Org mode is probably the best thing happened to the mankind. j/k By default Org mode doesn’t look very nice, at least not as a word processor. Our goal is to make it look like one.

  • Enable indentation(org-indent-mode). To control individual files, use #+STARTUP: indent or #+STARTUP: noindent .
    (setq org-startup-indented t)
        
  • Set a conservative indentation, By default the value is set to 2
    (setq org-indent-indentation-per-level 2)
        
  • Emacs shouldn’t add whitespace to indent text.
    (setq org-adapt-indentation nil)
        
  • RETURN will follow links in org mode.
    (setq org-return-follows-link  t)
        
  • Show inline images in org mode.
    (setq org-display-remote-inline-images 'cache) ;; enable caching
        
  • For shorthand completions, lile <s-TAB for source code blocks.
    (require 'org-tempo)
        

Org TOC

Create table of contents for Org files. Usage:

  • Add table of content tags such as TOC_2 and TOC_2_gh
  • While at the TOC entry call M-x org-open-at-point (C-c C-o) to

jump to the corresponding heading.

ref: toc-org

(use-package toc-org
  :hook (org-mode . toc-org-enable)
  :config
  (setq toc-org-hrefify-default "gh"))

Org Babel

Active Babel languages

(org-babel-do-load-languages
 'org-babel-load-languages
 '((C . t) (python . t) (haskell . t) ))

Org Hugo(ox-hugo)

ox-hugo helps me manage my website using org files.

I couldn’t install tomelr package, using use-package, so had to install it manually. Edit: 08/09/2024 - Installed it using straight.el

(use-package tomelr
  :straight (:host github :repo "kaushalmodi/tomelr" :files ("*.el"))
  :ensure t)
(use-package ox-hugo
  :ensure t   ;Auto-install the package from Melpa
  :pin melpa  ;`package-archives' should already have ("melpa" . "https://melpa.org/packages/")
  :after ox)    

Org Download

This nice package helps add images to an Org file in a better way.

Note: In order to copy from clipboard using org-download-clipboard, we need to install pngpaste using Homebrew.

(use-package org-download
  :straight t
  :bind
  ("C-c d" . org-download-clipboard)
  :config
  (org-download-enable))

(add-hook 'dired-mode-hook 'org-download-enable)

;; Set the image download directory
(setq org-download-image-dir eos/org-images-dir)

;; Set the image download to not depend on any headline
(setq org-download-heading-lvl nil)

Org Roam

A sophisticated note taking mechanishm. Essentially a clone of Roam-research running on Emacs.

(use-package org-roam
  :ensure t
  :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 c" . org-roam-capture)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n t" . org-roam-tag-add)
         ("C-c n b" . org-roam-buffer-toggle)
         ("C-c n j" . org-roam-dailies-capture-today))
  :pin melpa-stable
  :config
  (org-roam-setup))
(setq org-roam-directory eos/org-roam-dir)
Full text search with Deft

Deft is an Emacs mode for quickly browsing and filtering plain text notes.

(use-package deft
  :after org
  :bind
  ("C-c n d" . deft)
  :custom
  (deft-recursive t)
  (deft-use-filter-string-for-filename t)
  (deft-default-extension "org")
  (deft-directory org-roam-directory))
Backlink buffer

Org-roam backlink buffer, source

;; for org-roam-buffer-toggle
;; Recommendation in the official manual
(add-to-list 'display-buffer-alist
             '("\\*org-roam\\*"
               (display-buffer-in-direction)
               (direction . right)
               (window-width . 0.33)
               (window-height . fit-window-to-buffer)))

Org Roam UI

A visual interface for Org Roam.

(use-package org-roam-ui
  :straight
  (:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out"))
  :after org-roam
  ;;         normally we'd recommend hooking orui after org-roam, but since org-roam does not have
  ;;         a hookable mode anymore, you're advised to pick something yourself
  ;;         if you don't care about startup time, use
  ;;  :hook (after-init . org-roam-ui-mode)
  :config
  (setq org-roam-ui-sync-theme t
        org-roam-ui-follow t
        org-roam-ui-update-on-save t
        org-roam-ui-open-on-start t))

Org Export Settings(htmlize)

Org mode usually ships with this package, However in certain cases you might need to install it manually.

(use-package htmlize
  :ensure t
  :init
  (setq org-html-htmlize-output-type 'css)
  (setq org-html-htmlize-font-prefix "org-"))

Human Readable IDs

While exporting html, org-html-export-to-html function generates IDs for each header, so that it can get linked to from the Table of contents. However, the default generated IDs aren’t human-redable.

Also the default generated IDs can change every time you generate a new version, which can be annoying while hosting a public website.

I have found some hacks on the internet and Amit Patel’s implementation seemed like the simplest.

;; The only dependency
(use-package s
  :ensure t)

(defun eos/org-generate-custom-ids ()
  "Generate CUSTOM_ID for any headings that are missing one, but only in Org mode."
  (when (derived-mode-p 'org-mode)
    (let ((existing-ids (org-map-entries 
                         (lambda () (org-entry-get nil "CUSTOM_ID")))))

      (org-map-entries
       (lambda ()
         (when (org-at-heading-p)  ;; Ensure we're at a heading
           (let* ((custom-id (org-entry-get nil "CUSTOM_ID"))
                  (heading (org-heading-components))
                  (level (nth 0 heading))
                  (todo (nth 2 heading))
                  (headline (nth 4 heading))
                  (slug (eos/title-to-filename headline))
                  (duplicate-id (member slug existing-ids)))
             (when (and (not custom-id)
                        (< level 4)
                        (not todo)
                        (not duplicate-id))
               (message "Adding entry %s to %s" slug headline)
               (org-entry-put nil "CUSTOM_ID" slug)))))))))

;; Function to the after-save-hook only in Org mode
(add-hook 'org-mode-hook
          (lambda () 
            (add-hook 'after-save-hook 'eos/org-generate-custom-ids nil 'local)))

(defun eos/title-to-filename (title)
  "Convert TITLE to a reasonable filename."
  ;; Based on the slug logic in org-roam, but org-roam also uses a
  ;; timestamp, and I use only the slug. BTW "slug" comes from
  ;; <https://en.wikipedia.org/wiki/Clean_URL#Slug>
  (setq title (s-downcase title))
  (setq title (s-replace-regexp "[^a-zA-Z0-9]+" "-" title))
  (setq title (s-replace-regexp "-+" "-" title))
  (setq title (s-replace-regexp "^-" "" title))
  (setq title (s-replace-regexp "-$" "" title))
  title)

Run the function on save, while in org-mode.

(add-hook 'after-save-hook 'eos/org-generate-custom-ids)

Projectile

Projectile is instrumental in managing different projects and working on them.

(use-package counsel-projectile
  :after projectile
  :config (counsel-projectile-mode))

(counsel-projectile-mode)

(use-package projectile
  :diminish projectile-mode
  :config (projectile-mode)
  :custom ((projectile-completion-system 'ivy))
  :bind (:map projectile-mode-map
              ("C-c p" . projectile-command-map))
  :init
  ;; NOTE: Set this to the folder where you keep your Git repos!
  (setq projectile-project-search-path eos/project-dirs)
  (setq projectile-switch-project-action #'projectile-dired))

Version Control

Magit

The magical Git client for emacs.

Since I am using emacs-plus, In order for spotlight to find the emacs executable, I cpoied the executable to /Applications. However, After I did that, Magit showed an error message saying that it could not find the emacsclient executable. I had to set the variable with-editor-emacsclient-executable to “emacsclient” in order to fix the issue.

(setq-default with-editor-emacsclient-executable "emacsclient")
(use-package magit
  :ensure t
  :pin melpa-stable)

Diff-hl

Emacs port of the Sublime Git Gutter

(use-package diff-hl
  :straight (diff-hl :type git :host github :repo "dgutov/diff-hl")
  :hook ((prog-mode . diff-hl-mode)
         (org-mode . diff-hl-mode)
         (text-mode . diff-hl-mode))
  :config
  ;; Limit diff-hl to specific modes
  (setq diff-hl-global-modes '(not image-mode pdf-view-mode))

  ;; Use histogram diff algorithm
  (setq vc-git-diff-switches '("--histogram"))

  ;; Slightly more conservative delay before updating the diff
  (setq diff-hl-flydiff-delay 0.5)  ; default: 0.3

  ;; Perform async updates to avoid blocking Emacs
  (setq diff-hl-update-async t)

  ;; Do not show staged changes in real-time
  (setq diff-hl-show-staged-changes nil)

  ;; Enable on-the-fly diff highlighting and margin mode
  (diff-hl-flydiff-mode)
  (diff-hl-margin-mode))

Completions

Set up Ivy, Counsel, and Swiper for better completions.

(use-package counsel
  :straight t
  :diminish
  :bind (("C-s" . swiper)                         ;; Search using Swiper
         ("M-x" . counsel-M-x)                    ;; Enhanced M-x
         ("s-x" . counsel-M-x)                    ;; Super-X for M-x
         ("C-x C-f" . counsel-find-file)          ;; Enhanced find file
         ("C-x b" . ivy-switch-buffer)            ;; Show filtered buffers (code buffers)
         ("C-x B" . counsel-ibuffer)              ;; Show all buffers
         :map minibuffer-local-map
         ("C-x C-r" . counsel-minibuffer-history) ;; Access minibuffer history
         :map ivy-minibuffer-map
         ("C-j" . ivy-next-line)                  ;; Move down the list
         ("C-k" . ivy-previous-line)              ;; Move up the list
         ("C-f" . ivy-alt-done)                   ;; Complete selection
         :map ivy-switch-buffer-map
         ("C-k" . ivy-previous-line)              ;; Move up in buffer list
         ("C-d" . ivy-switch-buffer-kill)         ;; Kill selected buffer
         ("C-f" . ivy-done)                       ;; Complete buffer selection
         :map ivy-reverse-i-search-map
         ("C-k" . ivy-previous-line)              ;; Move up in reverse search
         ("C-d" . ivy-reverse-i-search-kill))     ;; Kill in reverse search
  :custom
  (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-only)
  :init
  (ivy-mode 1)                                    ;; Enable Ivy
  :config
  (counsel-mode 1))                               ;; Enable Counsel

C-x b doesn’t show emacs garbage buffers, C-x B shows all the buffers.

;; Configure ivy-switch-buffer (C-x b) to ignore certain buffers
(setq ivy-ignore-buffers
      '("\\` "
        "\\`\\*"
        "\\`magit"
        "\\`.+_archive\\'"
        "\\`TAGS\\'"
        "\\`COMMIT_EDITMSG\\'"
        "\\`MERGE_MSG\\'"
        "\\`undo-tree\\*\\'"))

Prescient settings for sorting and filtering.

;; Package: ivy-prescient
(use-package ivy-prescient
  :straight t
  :after counsel
  :custom
  (ivy-prescient-enable-filtering nil)           ;; Disable filtering
  :config
  ;; Uncomment the following line to persist sorting across sessions
  ;; (prescient-persist-mode 1)
  (ivy-prescient-mode 1))

;; Remove the "^" character from counsel-M-x
(setcdr (assoc 'counsel-M-x ivy-initial-inputs-alist) "")

Ivy rich

Ivy-rich for better Ivy interface

;; package: ivy-rich
(use-package ivy-rich
  :straight t
  :init
  (ivy-rich-mode 1))

;; All the icons + Ivy
(use-package all-the-icons-ivy-rich
  :straight t
  :ensure t
  :init (all-the-icons-ivy-rich-mode 1))

Hydra

(use-package hydra
  :defer t)

(defhydra hydra-text-scale (:timeout 4)
  "scale text"
  ("j" text-scale-increase "in")
  ("k" text-scale-decrease "out")
  ("f" nil "finished" :exit t))

Which-key

It’s a minor mode that shows kebindings for an incomplete command.

(use-package which-key
  :init
  (which-key-mode))

Treemacs

Unless it’s a large project, I don’t use Treemacs. However, it gets quite annoying while switching project since treemacs-follow-mode sometimes doesn’t work as intended.

(use-package treemacs
  :ensure t
  :defer t
  :init
  (with-eval-after-load 'winum
    (define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
  :config
  (progn
    (setq treemacs-collapse-dirs                 (if (treemacs--find-python3) 3 0)
          treemacs-deferred-git-apply-delay      0.5
          treemacs-width                         35)

    (treemacs-resize-icons 18)
    (treemacs-follow-mode t)
    (treemacs-project-follow-mode t)
    (treemacs-filewatch-mode t)))

;; (add-hook 'projectile-after-switch-project-hook 'treemacs-add-and-display-current-project-exclusively)

(use-package treemacs-evil
  :after (treemacs evil)
  :ensure t
  :pin melpa)

(use-package treemacs-projectile
  :after (treemacs projectile)
  :ensure t
  :pin melpa) 

RestClient

Postman for Emacs, A mode to run HTTP queries.

(use-package restclient
  :ensure t
  :pin melpa
  :mode (("\\.http\\'" . restclient-mode)))

Command-log-mode

Show event and command history, really helpful while debugging Emacs configs. Default binding to toggle is C-c M-c

(use-package command-log-mode
  :ensure t
  :bind
  ("C-c M-c" . clm/toggle-command-log-buffer)
  :config
  (global-command-log-mode t)
  :pin melpa)

Vterm

Probably the only usable terminal emulator for Emacs

(use-package vterm
  :ensure t
  :straight t)

System configuration

Emacs environment variables

Ensure Emacs env variables match system variables.

(use-package exec-path-from-shell
  :ensure t)

(when (memq window-system '(mac ns x))
  (exec-path-from-shell-initialize))

Code Completion

Tree-sitter

Tree-sitter is a parser generator tool and an incremental parsing library. It can build a concrete syntax tree for a source file and efficiently update the syntax tree as the source file is edited.

(setq treesit-language-source-alist
      '(  ; use `sort-lines' to sort
        (bash . ("https://github.com/tree-sitter/tree-sitter-bash"))
        (c . ("https://github.com/tree-sitter/tree-sitter-c"))
        (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp"))
        (css "https://github.com/tree-sitter/tree-sitter-css")
        (go "https://github.com/tree-sitter/tree-sitter-go")
        (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
        (html "https://github.com/tree-sitter/tree-sitter-html")
        (java "https://github.com/tree-sitter/tree-sitter-java")
        (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
        (json "https://github.com/tree-sitter/tree-sitter-json")
        (kotlin "https://github.com/fwcd/tree-sitter-kotlin")
        (python . ("https://github.com/tree-sitter/tree-sitter-python"))
        (rust "https://github.com/tree-sitter/tree-sitter-rust")
        (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "tsx/src"))
        (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" nil "typescript/src"))
        (typst "https://github.com/uben0/tree-sitter-typst")
        (vue "https://github.com/ikatyang/tree-sitter-vue")
        (yaml "https://github.com/ikatyang/tree-sitter-yaml")
        (toml "https://github.com/ikatyang/tree-sitter-toml")))


(defun eos/treesit-install-all-languages ()
  "Install all languages specified by `treesit-language-source-alist'."
  (interactive)
  (let ((languages (mapcar 'car treesit-language-source-alist)))
    (dolist (lang languages)
      (treesit-install-language-grammar lang)
      (message "`%s' parser was installed." lang)
      (sit-for 0.75))))

Custom functions to check and install tree-sitter.

;; Checks if a specific tree-sitter grammar file exists.
(defun treesit-grammar-installed-p (grammar-file)
  "Check if a specific tree-sitter GRAMMAR-FILE is installed."
  (file-exists-p grammar-file))

;; Ensures that a tree-sitter grammar for a given language is
;; installed, checking if the grammar file is present and if tree-sitter
;; is available.
(defun ensure-treesit-grammar-installed (language grammar-file)
  "Ensure a tree-sitter grammar for LANGUAGE is installed.
   GRAMMAR-FILE is the path to the grammar file."
  (unless (treesit-grammar-installed-p grammar-file)
    (when (and (fboundp 'treesit-available-p)
               (treesit-available-p))
      (treesit-install-language-grammar language))))

Programming stuff

This section has configurations for various programming language modes.

Eglot

Language Server Protocol can provide IDE like support for multiple programming languages on Emacs.

(setq eglot-ensure "C-c l")

Go

(use-package go-mode
  :ensure t
  :init
  (push '("\\.go\\'" . go-mode) auto-mode-alist))

Eglot hooks for Go

;; (defun lsp-go-install-save-hooks ()
;;   ;; Format before save
;;   (add-hook 'before-save-hook #'eglot-format-buffer t t)
;;   ;; Sort imports before save
;;   (add-hook 'before-save-hook #'eglot-code-action-organize-imports t t))


;; (add-hook 'go-mode-hook #'lsp-go-install-save-hooks)

;; Start eglot mode
;; (add-hook 'go-mode-hook 'eglot-ensure)

Rust

(use-package rust-mode
  :ensure t
  :init
  (push '("\\.rs\\'" . rust-mode) auto-mode-alist))

Haskell

Am I a 21st century Hippie now?

(use-package haskell-mode
  :straight (:host github :repo "haskell/haskell-mode")
  :mode "\\.hs\\'")

Typst

Seems like a decent alternative to LaTex.

;; Ensure Typst tree-sit grammar is installed
(ensure-treesit-grammar-installed 'typst (expand-file-name "tree-sitter/libtree-sitter-typst.dylib" user-emacs-directory))

(use-package typst-ts-mode
  :straight (:type git :host sourcehut :repo "meow_king/typst-ts-mode" :files (:defaults "*.el"))
  :custom
  ;; (optional) If you want to ensure your typst tree sitter grammar version is greater than the minimum requirement
  ;; Note this only checks and compares file modification time
  (typst-ts-mode-grammar-location (expand-file-name "tree-sitter/libtree-sitter-typst.dylib" user-emacs-directory)))

Modify typst-ts-compile to use absolute path.

(defun typst-ts-compile ()
  "Compile current Typst file."
  (interactive)
  (run-hooks typst-ts-compile-before-compilation-hook)
  (let ((full-file-path (expand-file-name buffer-file-name)))
    (add-hook 'compilation-finish-functions
              (typst-ts-compile--compilation-finish-function (current-buffer)))
    (compile
     (format "%s compile %s %s"
             typst-ts-compile-executable-location
             full-file-path
             typst-ts-compile-options)
     'typst-ts-compilation-mode)))

Custom functions to ask for root directory.

(defcustom typst-ts-root-folder nil
  "Root folder for Typst projects."
  :type 'directory
  :group 'typst-ts-compile)

(defun typst-ts-set-root-folder (folder)
  "Set the root folder for Typst projects."
  (interactive "DSelect Typst root folder: ")
  (setq typst-ts-root-folder folder)
  (setenv "TYPST_ROOT" folder)
  (message "Typst root folder set to: %s" folder))

(defun typst-ts-compile-with-root ()
  "Compile current Typst file with the root folder set."
  (interactive)
  (unless typst-ts-root-folder
    (call-interactively 'typst-ts-set-root-folder))
  (let ((default-directory typst-ts-root-folder))
    (typst-ts-compile)))

(with-eval-after-load 'typst-ts-mode
  (define-key typst-ts-mode-map (kbd "C-c C-c s") #'typst-ts-set-root-folder)
  (define-key typst-ts-mode-map (kbd "C-c C-c r") #'typst-ts-compile-with-root))

LaTex

Not sure if LaTex belongs here.

;; Install Auctex using straight.el
(use-package auctex
  :straight t
  :straight (:type git :host github :repo "emacs-straight/auctex")
  :config
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)
  (setq-default TeX-master nil)
  (setq TeX-PDF-mode t)
  (setq TeX-source-correlate-mode t)
  (setq TeX-source-correlate-start-server t)
  (setq TeX-view-program-selection '((output-pdf "PDF Tools"))
        TeX-view-program-list '(("PDF Tools" TeX-pdf-tools-sync-view))
        TeX-source-correlate-start-server t)

  (add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
  (add-hook 'LaTeX-mode-hook 'TeX-PDF-mode)
  (add-hook 'LaTeX-mode-hook 'TeX-fold-mode)
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (add-hook 'LaTeX-mode-hook 'turn-on-auto-fill)
  (add-hook 'LaTeX-mode-hook 'flyspell-mode)
  (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode))

Install pdf tools using straight.el with minimal configuration

(use-package pdf-tools
  :straight (:host github :repo "vedang/pdf-tools")
  :config
  ;; Initialize the PDF Tools package
  (pdf-tools-install)

  ;; Set PDF view mode to continuous mode
  (setq-default pdf-view-display-size 'fit-width))

;; Enable midnight mode for PDFs
(add-hook 'pdf-view-mode-hook 'pdf-view-midnight-minor-mode)

;; Set keybinding to install PDF Tools
(global-set-key (kbd "C-c p") 'pdf-tools-install)
  • Latex Preview Pane
    (use-package latex-preview-pane :straight t)
        

Yaml

Emacs should automatically switch on to yaml-mode while editing yml or yaml files.

(use-package yaml-mode
  :ensure t
  :init
  (push '("\\.yaml$" . yaml-mode) auto-mode-alist))

Markdown

Well, most readme files are markdown anyways.

(use-package markdown-mode
  :straight (:host github :repo "jrblevin/markdown-mode")
  :mode "\\.md\\'")

Lua

Because every damn thing needs lua for configuration.

(use-package lua-mode
  :straight (:host github :repo "immerrr/lua-mode")
  :mode "\\.lua\\'")

Code Execution

Makefile

Emacs helpers to run things from makefiles

(use-package makefile-executor
  :straight (:host github :repo "Olivia5k/makefile-executor.el")
  :config
  (add-hook 'makefile-mode-hook 'makefile-executor-mode))

Non-Human Intelligence

Copilot

(use-package copilot
  :straight (:host github :repo "copilot-emacs/copilot.el" :files ("*.el"))
  :ensure t
  :hook (prog-mode . copilot-mode)
  :bind (:map copilot-completion-map
              ("<tab>" . 'copilot-accept-completion-by-word)
              ("TAB" . 'copilot-accept-completion-by-word)
              ("C-TAB" . 'copilot-accept-completion)
              ("C-<tab>" . 'copilot-accept-completion)))

;; Suppress warnings from copilot
(setq warning-suppress-types '((copilot)))

Real life stuff

Emacs can do more than just text editing. Some are better, some are not.

Music Player

Emacs as a music player? Why not?

Honestly, I am really sick of the subscription model for music that randomly removes music from my playlist, because they lost the rights to it. I have decided to go back to the old school way of managing music myself.

(use-package ready-player
  :straight (ready-player :type git :host github :repo "xenodium/ready-player")
  :ensure t
  :config
  (ready-player-mode +1)
  :bind (("C-c m h" . 'ready-player-toggle-shuffle)
         ("C-c m s" . 'ready-player-search)
         ("C-c m q" . 'ready-player-quit)
         ("C-c m f" . 'ready-player-seek-forward)
         ("C-c m b" . 'ready-player-seek-backward)))


(setq ready-player-my-media-collection-location eos/music-dir)
(setq ready-player-set-global-bindings nil)

;; Reload the music collection
(defun ready-player-reload-music-collection ()
  "Reload the music collection."
  (interactive)
  (setq ready-player-reload-media-collection eos/music-dir))

Miscellaneous

Custom Functions

  • eos/toggle-debug-mode - Toggle debug mode on and off.
    (defun eos/toggle-debug-mode ()
      "Toggle debug-on-error mode."
      (interactive)
      (setq debug-on-error (not debug-on-error))
      (if debug-on-error
          (message "Debug mode enabled")
        (message "Debug mode disabled")))
    
    ;; Default enable debug-on-error mode
    (setq debug-on-error t)
        
  • eos/reload-config to reload init.el.
    (defun eos/reload-emacs-config ()
      "Reload the Emacs configuration file (init.el)."
      (interactive)
      (load-file (expand-file-name "~/.emacs.d/init.el")))
        
  • eos/open-config-file to open config.org.
    (defun eos/open-emacs-config ()
      "Open the Emacs configuration file (config.org)."
      (interactive)
      (find-file "~/.emacs.d/config.org"))
        
  • eos/highlight-comments to highlight comments. It’s tricky to revert back to the default settings, so either re-apply the theme or reload the config.
    (defun eos/toggle-highlight-comments ()
      "Set the style for highlighted comments with custom foreground, background, and slant."
      (interactive)
      (set-face-attribute 'font-lock-comment-face nil
                          :foreground "#4A90E2"
                          :background "#E6F7FF"
                          :slant 'italic))
        
  • eos/toggle-transparency to toggle transparency.
    (defun eos/toggle-transparency ()
      "Toggle between transparent and opaque Emacs frames."
      (interactive)
      (if transparency-enabled
          (progn
            (set-frame-parameter (selected-frame) 'alpha '(100 . 100))
            (setq transparency-enabled nil)
            (message "Transparency disabled"))
        (progn
          (set-frame-parameter (selected-frame) 'alpha
                               `(,transparency-level-active . ,transparency-level-inactive))
          (setq transparency-enabled t)
          (message "Transparency enabled"))))
        

Custom keybindings

Custom keybindings for emacs. This section is expected to be edited frequently, as my requirements evolve.

;; Keybindings
(global-set-key (kbd "<escape>") 'keyboard-escape-quit)          ;; ESC to quit prompts

;; Custom commands
(global-set-key (kbd "C-c e c") 'eos/open-emacs-config)          ;; Open config.org
(global-set-key (kbd "C-c e d") 'eos/toggle-debug-mode)          ;; Toggle debug mode
(global-set-key (kbd "C-c e f") 'treemacs)                       ;; Open Treemacs
(global-set-key (kbd "C-c e h") 'eos/highlight-comments)         ;; Highlight comments
(global-set-key (kbd "C-c e r") 'eos/reload-emacs-config)        ;; Reload init.el
(global-set-key (kbd "C-c e s") 'eos/toggle-transparency)        ;; Toggle transparency
(global-set-key (kbd "C-c e t") 'counsel-load-theme)             ;; Switch themes