All I wanted was to use magit today, but Spacemacs, which I have used for years, wouldn’t allow me. I could not figure out what the problem was—it likely had something to with dependencies that were out of sync. Failing to resolve the issue, I decided to reinstall emacs and configure it from scratch (with some great LLM help). The fruits of this tinkering is in the init.el file below. It is only about 160 lines long, but covers the vast majority of my requirements surprisingly well. This is a brief log of how this config file came about.
Installation
I removed and reinstalled the latest version of emacs.
sudo pacman -Rs emacs
sudo pacman -S emacs
This installed the base version of emacs-30.2. My Home folder now had an empuy .emacs.d folder.
Adapting the System
- Vim Keybindings: Emacs greets you with links to tutorials to help you get started. But I wanted nothing short of Evil (an emacs-vim layer), something Spacemacs had accustomed me to. Once the setup was complete and I could use Vim keybindings to move around in Emacs, everything became much easier.
- Command Completetion: Typing out full commands was the next issue to mitigate. Packages like vertico, orderless, and marginalia replicated all the command completion features offered by Spacemacs.
- Theme and Font: The ‘Dracula’ theme was easy to apply. But changing the font was a little tricky. It is easy to not specify the exact font or provide the wrong size and result in Emacs not loading.
- Version Control: Finally, I got magit set up. From here on, everything was version-controlled; any time a set of changes worsened my init, I reverted back.
- Syntax Highlighting: Got the syntax highlights for Python, Julia, and Markdown.
- Tweaks: My setup had already started looking pretty good by now. Next, I added small tweaks: fullscreen upon startup, line numbering, word-wrapping, etc.
Complete Configuration
Below is the full init.el file.
;;-------------------------------------
;; PART 1. STARTUP
;;-------------------------------------
(add-to-list 'default-frame-alist '(fullscreen . maximized))
(require 'package)
(setq package-archives
'(("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
;;-------------------------------------
;; PART 2. GENERAL SETTINGS
;;-------------------------------------
(global-visual-line-mode 1)
(column-number-mode 1)
(setq display-line-numbers-type 'relative)
(global-display-line-numbers-mode 1)
(recentf-mode 1)
(setq recentf-max-saved-items 200)
(setq recentf-max-menu-items 25)
(setq recentf-auto-cleanup 'never)
;;-------------------------------------
;; PART 3. EVIL KEYBINDINGS
;;-------------------------------------
(use-package evil
:init
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
(setq evil-want-C-i-jump nil)
:config
(evil-mode 1))
(use-package evil-collection
:after evil
:config
(evil-collection-init))
(use-package general
:demand t
:config
(general-evil-setup t)
(general-create-definer my/leader
:states '(normal visual motion)
:prefix "SPC"
:global-prefix "C-SPC")
(my/leader
"f f" '(find-file :which-key "Find file")
"f s" '(save-buffer :which-key "Save file")
"b b" '(switch-to-buffer :which-key "Switch buffer")
"g g" '(magit-status :which-key "Magit")
"q q" '(save-buffers-kill-terminal :which-key "Quit Emacs")
"f r" '(recentf-open-files :which-key "recent files")))
;;-------------------------------------
;; PART 4. COMMAND COMPLETION
;;-------------------------------------
(use-package vertico
:init
(vertico-mode 1))
(use-package orderless
:custom
(completion-styles '(orderless basic))
(completion-category-defaults nil)
(completion-category-overrides
'((file (styles basic partial-completion)))))
(use-package marginalia
:init
(marginalia-mode 1))
(use-package which-key
:init
(which-key-mode 1)
:custom
(which-key-idle-delay 0.2))
;;-------------------------------------
;; PART 5. APPEARANCE
;;-------------------------------------
(use-package dracula-theme
:config
(load-theme 'dracula t))
(set-face-attribute 'default nil
:family "JetBrainsMono Nerd Font Mono"
:height 160)
;;-------------------------------------
;; PART 6. PROGRAMMING
;;-------------------------------------
(use-package magit
:ensure t
:bind ("C-x g" . magit-status))
(setq treesit-language-source-alist
'((python "https://github.com/tree-sitter/tree-sitter-python")
(julia "https://github.com/tree-sitter/tree-sitter-julia")))
(setq major-mode-remap-alist
'((python-mode . python-ts-mode)))
(use-package python
:ensure nil)
(use-package julia-mode
:ensure t
:mode ("\\.jl\\'" . julia-mode))
(use-package markdown-mode
:mode ("\\.md\\'" . markdown-mode)
:init
(setq markdown-command "pandoc"))
(use-package adaptive-wrap
:hook (visual-line-mode . adaptive-wrap-prefix-mode))
;;-------------------------------------
;; PART 0. AUTO-GENERATED
;;-------------------------------------
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(package-selected-packages
'(adwaita-dark-theme dracula-theme evil-collection general julia-mode
magit marginalia orderless vertico))
'(warning-suppress-types '((use-package))))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
Thoughts
This was an interesting exercise. I have a better understanding of how Emacs works. I have just the packages and shortcuts I need. There is no complicated abstraction layer: everything is limited to the .emacs.d/init.el file. It’s quick. It’s not overwhelming.
I still have to make Emacs get closer to being an IDE. I still have to make some Org related tweaks. It’s probably good to add a spell-checker at some point rather than porting documents over to Obsidian for that. Over time, I expect my init file to grow. But it is good to have this snapshot of a minimal working configuration to return to should my setup ever get too complicated.