Skip to content

Instantly share code, notes, and snippets.

@skiletro
Last active February 6, 2024 20:50
Show Gist options
  • Save skiletro/75f5e16e48b43b6743e8085885ca3215 to your computer and use it in GitHub Desktop.
Save skiletro/75f5e16e48b43b6743e8085885ca3215 to your computer and use it in GitHub Desktop.
emacs config

Emacs Configuration

Last updated: <2024-02-06 Tue>

Welcome to my Emacs configuration. It is very much a work in progress.

Table of Contents

Todo

Finish dashboard

  • [ ] Make some nice logo/graphic

Auto-load LSP when opening supported files

Add more language servers

  • [X] Python
  • [ ] JavaScript/TypeScript
  • [ ] Go
  • [ ] Haskell
  • [ ] Yuck
  • [ ] Nix
  • [ ] HTML
  • [X] C++
  • [ ] CSS + SASS/SCSS

Learn and properly utilise dired

Look up resources on YouTube, etc.

Initial Configuration

Disclaimer

;; NOTE: init.el is generated using config.org. Please edit that file in Emacs.

Startup Performance

(setq gc-cons-threshold (* 85 1000 1000))

Packages

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

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

;; Init 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) ; Downloads packages if evaluated

See how long Emacs takes to load

This is a really useful section as it prints what is being loaded and when into *Messages*.

;; (setq use-package-verbose t)
(defun skil/display-startup-time ()
  (message "Emacs loaded %d packages in %s with %d GCs."
           (length package-activated-list)
           (format "%.3f seconds"
                   (float-time
                    (time-subtract after-init-time before-init-time)))
           gcs-done))
(add-hook 'emacs-startup-hook #'skil/display-startup-time)

Better GC Strategy

(use-package gcmh
  :init (gcmh-mode 1))

Decluttering

(setq user-emacs-directory "~/.cache/emacs")
(use-package no-littering)

(setq auto-save-file-name-transforms
      `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))

(setq custom-file (concat user-emacs-directory "/custom.el"))
;; (load-file custom-file)

Miscellaneous

;; Fixes a little bug on Windows
(set-language-environment "UTF-8")

;; Sets the backup location to the emacs cache directory (defined above)
(setq backup-directory-alist `(("." . ,(expand-file-name "file-backups" user-emacs-directory))))

UI Configuration

Basic Settings

(scroll-bar-mode -1) ; Disable visible scrollbar
(tool-bar-mode -1)   ; Disable the toolbar
(tooltip-mode -1)    ; Disable tooltips
(set-fringe-mode 10) ; Give some breathing room
(menu-bar-mode -1)   ; Disable the menu bar

(setq ring-bell-function 'ignore) ; Get rid of the bell sound

(column-number-mode) ; Column and row number in modeline
(global-display-line-numbers-mode t) ; Line numbers

(pixel-scroll-precision-mode t) ; Scroll through images without it jumping everywhere

(setq confirm-kill-emacs 'y-or-n-p) ; Confirmation on close

;; Disable line numbers for some modes
(dolist (mode '(term-mode-hook
                shell-mode-hook
                eshell-mode-hook))
  (add-hook mode (lambda () (display-line-numbers-mode 0))))

;; Change window name to something simpler
(setq frame-title-format "%b - Emacs")

Theme Configuration

(use-package catppuccin-theme
  :init (load-theme 'catppuccin :no-confirm))

Font Configuration

(set-face-attribute 'default nil :font "Iosevka NF" :height 120)
(set-face-attribute 'fixed-pitch nil :font "Iosevka NF" :height 120)
(set-face-attribute 'variable-pitch nil :font "Bahnschrift" :height 120)

Dashboard

Dashboard is a packge which creates, as the name suggests, a custom dashboard that starts on load. It can show a ton of information such as recent files, and things on your agenda.

(use-package dashboard
  :config (dashboard-setup-startup-hook))
(setq dashboard-buffer-name "*dashboard*"
      dashboard-banner-logo-title nil ; Subtitle
      dashboard-startup-banner 'logo
      dashboard-center-content t
      dashboard-display-icons-p t
      dashboard-set-heading-icons t
      dashboard-set-file-icons t
      dashboard-items '((recents . 5)
                        (bookmarks . 3)
                        (projects . 5)))

Modeline

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

Ivy and Counsel

(use-package ivy
  :diminish
  :bind (("C-s" . swiper)
         :map ivy-minibuffer-map
         ("TAB" . ivy-alt-done)
         ("C-l" . ivy-alt-done)
         ("C-j" . ivy-next-line)
         ("C-k" . ivy-previous-line)
         :map ivy-switch-buffer-map
         ("C-k" . ivy-previous-line)
         ("C-l" . ivy-done)
         ("C-d" . ivy-switch-buffer-kill)
         :map ivy-reverse-i-search-map
         ("C-k" . ivy-previous-line)
         ("C-d" . ivy-reverse-i-search-kill))
  :config (ivy-mode 1))

(use-package ivy-rich
  :after ivy
  :init (ivy-rich-mode 1))

;; More completion functions for Ivy
(use-package counsel
  :bind (("M-x" . counsel-M-x)
         ("C-x b" . counsel-ibuffer)
         ("C-x C-f" . counsel-find-file)
         :map minibuffer-local-map
         ("C-r" . 'counsel-minibuffer-history))
  :config (setq ivy-initial-inputs-alist nil)) ;; Don't start searches with ^

;; M-x Enhancement (adds history with no extra config)
(use-package ivy-prescient
  :after counsel
  :custom
  (ivy-prescient-enable-filtering nil)
  :config
  (prescient-persist-mode 1)
  (ivy-prescient-mode 1))

Which Key

(use-package which-key
  :defer 0
  :diminish which-key-mode
  :config
  (which-key-mode)
  (setq which-key-idle-delay 0))

Keybindings

This configuration uses evil-mode to emulate vim keybindings. General.el is also used to add further keybindings that integrate well with which-key.

General.el

(use-package general
  :config
  (general-create-definer skil/leader-keys
    :keymaps '(normal insert visual emacs)
    :prefix "SPC"
    :global-prefix "C-SPC"))
(skil/leader-keys
  "b"  '(:which-key "buffer")
  "b." '(counsel-switch-buffer :which-key "Switch buffer")
  "bn" '(next-buffer :which-key "Next buffer")
  "bN" '(skil/new-empty-buffer :which-key "New empty buffer")
  "bp" '(previous-buffer :which-key "Previous buffer")
  "bk" '(kill-this-buffer :which-key "Kill current buffer")
  "bs" '(save-buffer :which-key "Save current buffer")

  "f"  '(:which-key "file")
  "ff" '(find-file :which-key "Find file")

  "q"  '(:which-key "quit/kill")
  "qq" '(evil-quit :which-key "Quit Emacs"))

Evil Mode

(use-package evil
  :init
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil)
  (evil-mode)
  (evil-set-undo-system 'undo-redo)
  :config
  (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))

;; Auto configure modes with vim bindings 
(use-package evil-collection
  :after evil
  :config
  (evil-collection-init))

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

Pressing ESC Closes Prompts Properly

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

Help Commands

Helpful

Helpful adds a lot of useful information to Emacs’ describe- command buffers.

(use-package helpful
  :commands (helpful-callable helpful-variable helpful-command helpful-key)
  :custom
  (counsel-describe-function-function #'helpful-callable)
  (counsel-describe-variable-function #'helpful-variable)
  :bind
  ([remap describe-function] . counsel-describe-function)
  ([remap describe-command] . helpful-command)
  ([remap describe-variable] . counsel-describe-variable)
  ([remap describe-key] . helpful-key))
(skil/leader-keys
  "h"  '(:which-key "help")
  "hf" '(describe-function :which-key "Describe function")
  "hc" '(describe-command :which-key "Describe command")
  "hv" '(describe-variable :which-key "Describe variable")
  "hk" '(describe-key :which-key "Describe-key"))

Evil Tutor

Vimtutor adapted for Evil and wrapped in a major mode

(use-package evil-tutor
  :commands (evil-tutor-start))

Org Mode

Declutter this massive fuck off codeblock

(use-package org
  :commands (org-capture org-agenda)
  :hook
  (org-mode . skil/org-mode-setup)
  (org-mode . skil/org-icons-setup)
  :custom
  (org-ellipsis "")
  (org-directory "~/org/")
                                        ;(org-agenda-files '("~/org/tasks.org"))
  (org-hide-emphasis-markers t)
  (org-return-follows-link t))

(defun skil/org-mode-setup ()
  (org-indent-mode)
  (visual-line-mode 1))

(defun skil/org-icons-setup ()
  (interactive)
  (setq prettify-symbols-alist
        (mapcan (lambda (x) (list x (cons (upcase (car x)) (cdr x))))
                '(("TODO" . "")
                  ("WAIT" . "")
                  ("NOPE" . "")
                  ("DONE" . "")
                  ("#+property:" . "")
                  (":properties:" . "")
                  (":end:" . "")
                  ("#+startup:" . "")
                  ("#+title: " . "")
                  ("#+results:" . "")
                  ("#+name:" . "")
                  ("#+filetags:" . "")
                  ("#+html_head:" . "")
                  ("#+subtitle:" . "")
                  ("#+author:" . "")
                  (":Effort:" . "")
                  ("schedule:" . "")
                  ("deadline:" . "")
                  (":toc:" . ""))))
  (prettify-symbols-mode 1))

Open Files in Same Window

(setq org-link-frame-setup
      '((vm . vm-visit-folder-other-frame)
        (vm-imap . vm-visit-imap-folder-other-frame)
        (gnus . org-gnus-no-new-news)
        (file . find-file)
        (wl . wl-other-frame)))

Org Modern

(use-package org-modern
  :custom
  (org-hide-emphasis-markers t)
  (org-modern-table nil)
  (org-modern-tag nil)
  (org-modern-keyword nil)
  (org-modern-todo nil)
  (org-modern-block-fringe nil)
  :hook
  (org-mode . org-modern-mode)
  (org-agenda-finalize . org-modern-agenda))

Configure Babel Languages

(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((emacs-lisp . t)
     (python . t)))

  (push '("conf-unix" . conf-unix) org-src-lang-modes))

Org Roam

(use-package org-roam
  :custom
  (org-roam-directory (file-truename "~/org/roam/"))
  :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 i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ("C-c n j" . org-roam-dailies-capture-today)))
(skil/leader-keys
  "nr"  '(:which-key "org-roam")
  "nri" '(org-roam-node-insert :which-key "Insert node")
  "nrf" '(org-roam-node-find :which-key "Find node"))

Structure Templates

(with-eval-after-load 'org
  (require 'org-tempo)

  (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")))

Auto Tangle Configuration Files

(defun skil/org-babel-tangle-config ()
  (when (string-equal (buffer-file-name)
                      (expand-file-name "~/.emacs.d/config.org"))
    (let ((org-confirm-babel-evaluate nil))
      (org-babel-tangle))))

(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'skil/org-babel-tangle-config)))

Auto Table of Contents

(use-package toc-org
  :hook (org-mode . toc-org-mode))

LaTeX Fragment Previews

This is used in conjunction with the built in fragment LaTeX fragment previewer.

  • On NixOS, the package texlive.combined.scheme-medium is recommended.
  • On other distros, make sure you have the dvipng, dvisvgm (Recommended), or convert commands installed

This package automatically toggles previews on and off when you have the cursor over them.

(use-package org-fragtog
  :hook (org-mode . org-fragtog-mode))

This block moves the place being used to store LaTeX previews to the emacs cache directory (defined earlier), as well as changes the LaTeX previews to use svg instead of png.

(setq org-preview-latex-image-directory (concat user-emacs-directory "/latex-images"))
;; (setq org-preview-latex-default-process 'dvisvgm)
(setq org-preview-latex-default-process 'dvipng) ; Bug with dvisvmg at the moment where text wrapped in \{text} isn't being rendered correctly.  

Drag-and-Drop/Pasting Images

This extension facilitates moving images from point A to point B. Point A (the source) can be:

  • An image inside your browser that you can drag to Emacs.
  • An image on your file system that you can drag to Emacs.
  • A local or remote image address in kill-ring. Use the org-download-yank command for this. Remember that you can use “0 w” in dired to get an address.
  • A screenshot taken using gnome-screenshot, scrot, gm, xclip (on Linux), screencapture (on OS X) or, imagemagick/convert (on Windows). Use the org-download-screenshot command for this. Customize the backend with org-download-screenshot-method.
(use-package org-download
  :after org
  :config
    (setq-default org-download-image-dir "./_assets") 
  :hook (dired-mode-hook . org-download-enable))

Development

Languages

Language Servers

(use-package lsp-mode
  :commands (lsp lsp-deferred)
  :init (setq lsp-keymap-prefix "C-c l")
  :config (lsp-enable-which-key-integration t))

(use-package company
  :after lsp-mode) ; auto complete-at-point
(use-package company-box ; nicer looking company mode
  :hook (company-mode . company-box-mode))
(use-package lsp-ui
  :hook (lsp-mode . lsp-ui-mode)
  :custom
  (lsp-ui-doc-position 'bottom))
(use-package lsp-ivy
  :after lsp)

C++

For this language, an external package is required.

  • On NixOS, add the ccls package
  • On other distros, it will be called something similar to ccls.
(use-package ccls
  :after lsp
  :hook (c++-mode . lsp-deferred))

Python

For this language, an external package is required.

  • On NixOS, add the nodePackages.pyright package
  • On any other distro, install by typing npm install --global pyright
(use-package lsp-pyright
  :after lsp
  :hook (python-mode . lsp-deferred))

Projectile

Project Management

(use-package projectile
  :diminish projectile-mode
  :config (projectile-mode)
  :custom ((projectile-completion-system 'ivy))
  :bind-keymap
  ("C-c p" . projectile-command-map)
  :init
  (setq projectile-switch-project-action #'projectile-dired))
(use-package counsel-projectile
  :after projectile
  :config (counsel-projectile-mode))
(skil/leader-keys
  "p"  '(projectile-command-map :which-key "project"))

Magit

The git porcelain! Allows for interaction with git using Emacs and its’ bindings.

(use-package magit
  :commands (magit-status magit-get-current-branch)
  :custom
  (magit-display-buffer function #'magit-display-buffer-same-window-except-diff-v1))

Commenting

(use-package evil-nerd-commenter
  :bind ("M-/" . evilnc-comment-or-uncomment-lines))
(skil/leader-keys
  "bc" '(evilnc-comment-or-uncomment-lines :which-key "Comment/uncomment code"))

Rainbow Delimiters

Can also be referred to as rainbow brackets or rainbow parentheses, it colourises nested delimiters according to their depth

(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))

Functions

Here lies various miscellaneous functions that are used

New Empty Buffer

(defun skil/new-empty-buffer ()
  "Create a new empty buffer."
  (interactive)
  (let ((xbuf (generate-new-buffer "*new*")))
    (switch-to-buffer xbuf)
    (funcall initial-major-mode)
    xbuf))

Temporary Text Scaling

This is similar to Doom Emacs’ “Big Mode”

(defvar skil/is-big nil)
(defun skil/temp-text-scaling ()
  "Toggles temporary text scaling (a.k.a., big text mode"
  (interactive)
  (if skil/is-big
      (progn
        (text-scale-increase 0)
        (setq skil/is-big nil))
    (progn
      (text-scale-increase 2)
      (setq skil/is-big t))))

Reload Emacs

"Reloads Emacs init.el"
(defun skil/reload-init-file ()
  (interactive)
  (load-file user-init-file))

Extras

Runtime Performance

Put the GC threshold back down so that GC happens more frequently once startup has completed. You make GC pauses faster by decreasing the threshold. This snippet needs to be at the bottom of the configuration file.

(setq gc-cons-threshold (* 2 1000 1000))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment