Skip to content

hnarayanan/dotemacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GNU Emacs Configuration

I have been using Emacs since I was a late teen (I am now in my forties), but I have mostly used Emacs as a plain text editor for much of this time. My customisation of Emacs was thus limited to a handful of snippets cribbed from the internet. I didn’t use many packages or see the need for a package repository to pull them from.

Only very recently (end 2023) did I start to see the true power of Emacs Lisp, and the draw of environments like Org Mode and Magit. And as I’ve explored these things, my Emacs configuration has correspondingly started to grow. This file collects all these ideas in one place. The structure of this file — which is an Org Mode file that exports to actual configuration files — was very inspired by a much richer variant by Prot.

Please write to me if you need any help with how it’s used or if you’d like to suggest any improvements.

Early Initialisation

There are some things (mostly pertinent to the base UI) that need to be set really early in Emacs’ startup. This is so that the UI doesn’t first show up uncustomised, and then “flash” as it redraws based on any later UI customisation (such as a change of theme).

This early initialisation configuration goes into a handily-named file called early-init.el.

Start with a clean slate

Depending on your system, there might be some default configuration shared by other Emacs users in a file called default.el. To ensure our Emacs behaves consistently everywhere, we ignore this and start from a blank slate.

(setq inhibit-default-init t)

Remove unused GUI elements

There are a few elements like a graphical menu and a scroll-bar that are useful for beginners when first getting acquainted with Emacs. But as you get more experienced navigating the app with a keyboard, they get less useful. As a more advanced user, I remove them from the UI to let me focus more on the content.

(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)

In the snippet above, -1 is convention for “don’t load” the corresponding mode.

Set the initial background colour to match my currently used theme

Emacs tends to start with a default (white) colour when first loaded, which is then refreshed later as a custom theme is loaded. We avoid this by hard-coding the background colour to the same value as a known custom theme. (I do not know how to make it dynamic for now, but this is not a problem as I rarely change my base theme.)

(setq default-frame-alist
      ;; bg-main from the modus-vivendi-tinted theme is hard-coded below
      (append default-frame-alist '((background-color . "#0d0e1c"))))

Here, default-frame-alist is a list of key-value pairs that help set attributes for newly created frames (what you’d call a window). We’re extending the default by this background colour.

OS-Specific Customisation

I generally use Emacs on macOS, and this needs me to tweak it a tiny bit to my liking. For now, I’ve hard-coded my configuration to assume I’m running on macOS, but I also use Linux from time to time and in that context will need to add a conditional.

Set the Command key to function as the Meta key on macOS

Emacs has two primary modifier keys, the Control key (C) and the Meta key (M). M is traditionally mapped to Alt on most keyboards, but on a Mac, Command is so much more comfortable.

(setq-default mac-command-modifier 'meta)

Core settings

;; put emacs-derived customisations into a separate file
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

;; set environment variables
(setenv "LC_ALL" "C")

Startup UI

;; default to better frame titles
(setq-default frame-title-format
      (concat  "%b - emacs@" (system-name)))

;; remove splash screen on start-up
(setq inhibit-startup-screen t)

;; hide scratch message on start-up
(setq-default initial-scratch-message "")

;; default to text-mode
(setq-default initial-major-mode 'text-mode)
(setq-default default-major-mode 'text-mode)

To Classify

What follows is simply the remainder of my existing configuration. I will break it out into sections and document it better in time.

;; copy selected text
(setq-default mouse-drag-copy-region t)

;; enable column number mode
(setq-default column-number-mode t)

;; enable visual feedback on selections
(setq-default transient-mark-mode t)

;; show the boundaries of the file
(setq-default indicate-buffer-boundaries 'right)

;; split buffers horizontally when opening multiple files
;; (setq-default split-width-threshold 0)

;; don't require two spaces after full stops to define sentences
(setq-default sentence-end-double-space nil)

;; show trailing spaces and empty lines
(setq-default show-trailing-whitespace t)
(setq-default indicate-empty-lines t)

;; enable up- and down-casing
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)

;; prevent extraneous tabs and use 2 spaces
(setq-default indent-tabs-mode nil)
(setq-default tab-width 2)

;; highlight matching pairs of parentheses
(setq-default show-paren-delay 0)
(show-paren-mode)

;; set default indentation for different languages
(setq c-default-style "bsd"
      c-basic-offset 2)
(setq sgml-basic-offset 2)

;; turn on interactive do
(ido-mode t)
(setq-default ido-enable-flex-matching t)
(setq-default ido-everywhere t)

;; enable flyspell-mode with an appropriate dictionary
(add-hook 'text-mode-hook 'flyspell-mode)
(setq ispell-dictionary "british")

;; setup ediff to have a neater layout
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

External package repository

;; load emacs' package system and add melpa repository
(require 'package)
(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

To Classify

What follows is simply the remainder of my existing configuration. I will break it out into sections and document it better in time.

;; configure useful packages with use-package
(use-package magit :ensure t)
(use-package unfill :ensure t)
(use-package smex :ensure t)
(use-package go-mode :ensure t)
(use-package julia-mode :ensure t)
(use-package php-mode :ensure t)
(use-package markdown-mode :ensure t)
(use-package yaml-mode :ensure t)
(use-package graphviz-dot-mode :ensure t)

(defun theme-custom-faces ()
  (modus-themes-with-colors
    (custom-set-faces
     ;; Add "padding" to the mode lines
     `(mode-line ((,c :box (:line-width 3 :color ,bg-mode-line-active))))
     `(mode-line-inactive ((,c :box (:line-width 3 :color ,bg-mode-line-inactive)))))))

(use-package modus-themes
  :ensure t
  :config

  (setq modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
        modus-themes-bold-constructs t
        modus-themes-italic-constructs t
        modus-themes-org-blocks 'gray-background)

  (setq modus-themes-common-palette-overrides
        '((bg-mode-line-active bg-blue-subtle)
          (fg-mode-line-active fg-main)
          (border-mode-line-active bg-blue-subtle)))

  (modus-themes-load-theme 'modus-vivendi-tinted)

  (define-key global-map (kbd "<f5>") #'modus-themes-toggle))

(add-hook 'modus-themes-after-load-theme-hook #'theme-custom-faces)

(setq org-edit-src-content-indentation 0)
(global-set-key (kbd "C-c a") 'org-agenda)
;; consider https://github.com/minad/org-modern
(use-package org-bullets
  :ensure t
  :config
  (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))
(setq org-agenda-files '("~/Notes/todo.org"))

;; setup corfu
(use-package corfu
  :ensure t
  :custom
  (corfu-cycle t)
  (corfu-separator ?\s)
  (corfu-scroll-margin 5)
  :init
  (global-corfu-mode))

(use-package emacs
  :init
  (setq completion-cycle-threshold 3)
  (setq tab-always-indent 'complete))

;; setup tree-sitter
(use-package tree-sitter
  :ensure t
  :config
  (global-tree-sitter-mode)
  (add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode))

(use-package tree-sitter-langs
  :ensure t
  :after tree-sitter)

;; configure a development environment for python
(use-package python
  :ensure t
  :hook ((python-mode . eglot-ensure)
         (python-mode . tree-sitter-hl-mode)))

(use-package mastodon
  :ensure t
  :config
  (setq mastodon-instance-url "https://hachyderm.io/"
        mastodon-active-user "harish")
  )

(use-package gptel
  :ensure t
  ;; :config
  ;; (setq mastodon-instance-url "https://hachyderm.io/"
  ;;       mastodon-active-user "harish")
  )

;; (add-hook 'after-init-hook 'global-company-mode)

;; enable smex
(global-set-key (kbd "M-x") 'smex)
(global-set-key (kbd "M-X") 'smex-major-mode-commands)
(global-set-key (kbd "C-c C-c M-x") 'execute-extended-command)

;; turn on auto-fill mode for LaTeX files
(add-hook 'tex-mode-hook 'turn-on-auto-fill t)

;; turn on YAML mode for YAML files
(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.yaml\\'" . yaml-mode))

;; turn on octave mode for M files
(add-to-list 'auto-mode-alist '("\\.m\\'" . octave-mode))

Improving the minibuffer

The minibuffer is the small interface at the bottom of the Emacs window where you can enter commands, input parameters, see results of these commands and so on. The internet suggests that with the following packages, it will be much more functional.

  • vertico
  • marginalia
  • orderless
  • consult
  • embark
  • embark-consult
  • wgrep
  • savehist
  • recentf

At the moment I only use interactive-do, which is awesome but also like 90 years old.

Possible outline

Core settings and early initialisation Fetch necessary packages Broad UI customisation

My custom functions

These are specific to my needs, and are likely not useful for other people. They are prefixed with my initials, hn-.

(defun hn-journal-todo (start-date end-date &optional prefix)
  "Generate a todo list for journal entries from START-DATE to END-DATE with an optional PREFIX."
  (interactive
   (list
    (read-string "Enter start date (YYYY-MM-DD): ")
    (read-string "Enter end date (YYYY-MM-DD): ")
    (read-string "Enter prefix: " "** Write entry for ")))
  (let* ((start-time (date-to-time start-date))
         (end-time (date-to-time end-date))
         (one-day (seconds-to-time 86400)) ; 24 hours * 60 minutes * 60 seconds
         (current-time start-time))
    (while (time-less-p current-time (time-add end-time one-day))
      (let ((entry-date (format-time-string "%A %d-%m-%Y" current-time)))
        (insert (format "%s%s\n" (or prefix "** Write entry for ") entry-date)))
      (setq current-time (time-add current-time one-day)))))

About

My local emacs configuration

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published