The Emacs Operating System
Table of Contents
- 1. Overview
- 2. Initialization
- 3. Package Management
- 4. Appearence
- 5. Functionality
- 6. System configuration
- 7. Code Completion
- 8. Programming stuff
- 9. Code Execution
- 10. Non-Human Intelligence
- 11. Real life stuff
- 12. Miscellaneous
1. 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.
2. Initialization
2.1. 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))))
2.2. Custom Variables
2.2.1. 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))
2.3. 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)))
3. Package Management
Setting up package managers and installing essential packages.
3.1. 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)
3.1.1. 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))
4. Appearence
4.1. All the Icons
Why we need all the icons? Because they are pretty.
(use-package all-the-icons :ensure t)
4.2. 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)
4.2.1. 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))
4.3. 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)
4.4. 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.")
4.5. 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))
4.6. 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))
4.7. 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))))
4.8. Indentation
Indent bars is a better package, however each bar is flanked by two dotted line and looks quite weird.
GitHub issue: https://github.com/jdtsmith/indent-bars/issues/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))
5. Functionality
5.1. 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))
5.2. 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)
5.2.1. Org TOC
Create table of contents for Org files. Usage:
- Add table of content tags such as
TOC_2
andTOC_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"))
5.2.2. Org Babel
Active Babel languages
(org-babel-do-load-languages 'org-babel-load-languages '((C . t) (python . t) (haskell . t) ))
5.2.3. 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)
5.2.4. 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)
5.2.5. 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)))
5.2.6. 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))
5.2.7. 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-"))
5.2.8. 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)
5.3. 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))
5.4. Version Control
5.4.1. 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)
5.4.2. 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))
5.5. 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) "")
5.5.1. 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))
5.5.2. 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))
5.5.3. Which-key
It's a minor mode that shows kebindings for an incomplete command.
(use-package which-key :init (which-key-mode))
5.6. 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)
5.7. RestClient
Postman for Emacs, A mode to run HTTP queries.
(use-package restclient :ensure t :pin melpa :mode (("\\.http\\'" . restclient-mode)))
5.8. 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)
5.9. Vterm
Probably the only usable terminal emulator for Emacs
(use-package vterm :ensure t :straight t)
6. System configuration
6.1. 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))
7. Code Completion
7.1. 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))))
8. Programming stuff
This section has configurations for various programming language modes.
8.1. Eglot
Language Server Protocol can provide IDE like support for multiple programming languages on Emacs.
(setq eglot-ensure "C-c l")
8.2. 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)
8.3. Rust
(use-package rust-mode :ensure t :init (push '("\\.rs\\'" . rust-mode) auto-mode-alist))
8.4. Haskell
Am I a 21st century Hippie now?
(use-package haskell-mode :straight (:host github :repo "haskell/haskell-mode") :mode "\\.hs\\'")
8.5. 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))
8.6. 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)
8.7. 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))
8.8. Markdown
Well, most readme files are markdown anyways.
(use-package markdown-mode :straight (:host github :repo "jrblevin/markdown-mode") :mode "\\.md\\'")
8.9. Lua
Because every damn thing needs lua for configuration.
(use-package lua-mode :straight (:host github :repo "immerrr/lua-mode") :mode "\\.lua\\'")
9. Code Execution
9.1. 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))
10. Non-Human Intelligence
10.1. 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)))
11. Real life stuff
Emacs can do more than just text editing. Some are better, some are not.
11.1. 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))
12. Miscellaneous
12.1. 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 reloadinit.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 openconfig.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"))))
12.2. 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