¶Introduction
This Org Mode document contains my configuration for Emacs, which is currently a work in progress. Changes made to the configuration here are automatically tangled when saved. As such, all configuration changes should be made here.
¶Early Init File
The configuration here gets placed in an early init file to prevent certain actions from occuring before getting to the main init file.
Since I am using straight.el The first thing we need to do is stop package.el from loading packages.
;; Prevent package.el from loading packages (setq package-enable-at-startup nil)
Here, we make sure we’re starting with a minimal user interface.
;; Disable the menu bar (menu-bar-mode -1) ;; Disable the tool bar (tool-bar-mode -1) ;; Disable the scroll bar (scroll-bar-mode -1) ;; Disable the splash screen (setq inhibit-splash-screen t)
¶Primary Init File
Here we specify the main Emacs configuration.
¶Init File Header
Add a note to the header to remind me not to edit that file.
;; NOTE: init.el is generated from emacs.org. Please edit that file instead!
¶Straight Package Management
I chose to use straight.el for my package management. I like the principles of the project, and changing package managers will help me stop simply copying other people’s configuration without trying to figure it out myself.
;; Bootstrap code needed to download and build straight.el (defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 6)) (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)) ;; Use straight.el for use-package expressions (straight-use-package 'use-package) ;; Automatically install packages without needing to specify so (setq straight-use-package-by-default t)
¶Clean Up Folders
no-littering avoids excessive clutter in the user-emacs-directory.
;; Use no-littering to automatically set common paths to the user-emacs-directory (use-package no-littering) ;; no-littering doesn't set this by default so we must place auto save ;; files in the same path as it uses for sessions (setq auto-save-file-name-transforms `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))) ;; Keep customization settings in a temporary file (setq custom-file (if (boundp 'server-socket-dir) (expand-file-name "custom.el" server-socket-dir) (expand-file-name (format "emacs-custom-%s.el" (user-uid)) temporary-file-directory))) (load custom-file t)
¶User Interface Configuration
¶Basic Setup
Here we make some general configuration tweaks to the user interface, such as transparency, line numbers, etc..
;; Use pixel wise frame size (setq frame-resize-pixelwise t) ;; Make frame transparency overridable (defvar jdw/frame-transparency '(97 . 97)) ; Disable tooltips (tooltip-mode -1) ; Give some breathing room (set-fringe-mode 10) ;; Set up the visible bell (setq visible-bell t) ;; Enable column and line number modes (column-number-mode) (global-display-line-numbers-mode t) ;; Set frame transparency (set-frame-parameter (selected-frame) 'alpha jdw/frame-transparency) (add-to-list 'default-frame-alist `(alpha . ,jdw/frame-transparency)) ;; Disable line numbers for some modes (dolist (mode '(org-mode-hook eshell-mode-hook)) (add-hook mode (lambda () (display-line-numbers-mode 0))))
¶Font Configuration
I am using the Fira Code fonts. Let’s set font sizes for various fonts.
NOTE: The font size will likely need to change from system to system.
;; Specify the font size (defvar jdw/default-font-size 120) (defvar jdw/default-variable-font-size 140) (defvar jdw/default-variable-font-weight 'regular) ;; Set font faces (defun jdw/set-font-faces () ;; Set the default face (set-face-attribute 'default nil :font "Fira Code Retina" :height jdw/default-font-size) ;; Set the fixed pitch face (set-face-attribute 'fixed-pitch nil :font "Fira Code Retina" :height jdw/default-font-size) ;; Set the variable pitch face (set-face-attribute 'variable-pitch nil :font "Fira Sans" :height jdw/default-variable-font-size :weight jdw/default-variable-font-weight) ;; Make commented text and keywords italics. ;; Your font must have an italic face available. (set-face-attribute 'font-lock-comment-face nil :slant 'italic) (set-face-attribute 'font-lock-keyword-face nil :slant 'italic)) ;; Uncomment the following line if line spacing needs adjusting. (setq-default line-spacing 0.12) ;; Needed if using emacsclient. Otherwise, your fonts will be smaller than expected. (add-to-list 'default-frame-alist '(font . "Fira Code Retina-12")) ;; Changes certain keywords to symbols, such as lamda! (setq global-prettify-symbols-mode t) ;; Call font face function differently if using daemon (if (daemonp) (add-hook 'after-make-frame-functions (lambda (frame) (with-selected-frame frame (jdw/set-font-faces)))) (jdw/set-font-faces))
¶Color Theme
doom-themes is a great set of themes with a lot of variety and support for many different Emacs modes, and the screenshots can help decide without manually booting up each theme to decide.
;; Use doom-themes for color themes (use-package doom-themes :config ;; Enable bold and italics (setq doom-themes-enable-bold t doom-themes-enable-italic t) ;; Load the Dracula theme (load-theme 'doom-gruvbox t) ;; Enable flashing mode-line on errors (doom-themes-visual-bell-config) ;; Corrects (and improves) org-mode's native fontification. (doom-themes-org-config))
¶Modeline
doom-modeline is a very attractive and rich (yet still minimal) mode line configuration for Emacs. The default configuration is quite good but you can check out the configuration options for more things you can enable or disable.
NOTE: The first time you load your configuration on a new machine, you’ll need to run M-x all-the-icons-install-fonts
so that mode line icons display correctly.
;; Use all-the-icons to view symbols in the modeline (use-package all-the-icons) ;; Use doom-modeline for a prettier modeline (use-package doom-modeline :hook (after-init . doom-modeline-mode) :custom ((doom-modeline-height 30) (doom-modeline-icon t)))
¶Dashboard
I kind of like the doom-emacs dashboard, so let’s try using out dashboard.
;; Install nerd icons (use-package nerd-icons) (use-package dashboard :ensure t :config (dashboard-setup-startup-hook) :custom ;; Set the title (dashboard-banner-logo-title "Welcome to Emacs Dashboard!") ;; Set the banner (dashboard-startup-banner 1) ;; Limit items that appear and specify what appears (dashboard-items '((recents . 5) (bookmarks . 5) (projects . 5) (agenda . 5))) ;; Display icons (dashboard-display-icons-p t) (dashboard-icon-type 'nerd-icons) (dashboard-set-heading-icons t) (dashboard-set-file-icons t) )
¶Leader Key Bindings with General
General allows leader key binding, which I will choose as SPC
.
(use-package general :config (general-evil-setup t) (general-create-definer jdw/leader-key-def :keymaps '(normal insert visual emacs) :prefix "SPC" :global-prefix "C-SPC") (general-create-definer jdw/ctrl-c-keys :prefix "C-c")) ;; Files (jdw/leader-key-def "f" '(:ignore t :which-key "files") "ff" 'find-file "fs" 'save-buffer) ;; Search (jdw/leader-key-def "s" '(:ignore t :which-key "search") "sb" 'consult-line "sr" 'query-replace) ;; Toggles (jdw/leader-key-def "t" '(:ignore t :which-key "toggles") "tt" '(consult-theme :which-key "choose theme"))
¶Evil Mode
Evil is a vi layer for Emacs. It changes a lot of key bindings and other features to be more like vi. I rarely use vi, but I do prefer the typical key bindings.
;; Add hooks for evil (defun jdw/evil-hook () (dolist (mode '(custom-mode eshell-mode git-rebase-mode term-mode)) (add-to-list 'evil-emacs-state-modes mode))) ;; Remind me to get back to the home row of keys (defun jdw/dont-use-arrows () (interactive) (message "Get back on the home row!")) ;; Use vi-like undo state preservation (use-package undo-tree :init (global-undo-tree-mode 1) :config ;; Prevent undo tree files from polluting your git repo (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) ;; Use evil (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) (setq evil-respect-visual-line-mode t) (setq evil-undo-system 'undo-tree) :config (add-hook 'evil-mode-hook 'jdw/evil-hook) (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) ;; 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) ;; Disable arrow keys in normal and visual modes (define-key evil-normal-state-map (kbd "<left>") 'jdw/dont-use-arrows) (define-key evil-normal-state-map (kbd "<right>") 'jdw/dont-use-arrows) (define-key evil-normal-state-map (kbd "<down>") 'jdw/dont-use-arrows) (define-key evil-normal-state-map (kbd "<up>") 'jdw/dont-use-arrows) (evil-global-set-key 'motion (kbd "<left>") 'jdw/dont-use-arrows) (evil-global-set-key 'motion (kbd "<right>") 'jdw/dont-use-arrows) (evil-global-set-key 'motion (kbd "<down>") 'jdw/dont-use-arrows) (evil-global-set-key 'motion (kbd "<up>") 'jdw/dont-use-arrows) ;; Set initial states (evil-set-initial-state 'messages-buffer-mode 'normal) (evil-set-initial-state 'dashboard-mode 'normal)) ;; Load in additional evil keybindings (use-package evil-collection :after evil :init (setq evil-collection-company-use-tng nil) ;; Is this a bug in evil-collection? :custom (evil-collection-outline-bind-tab-p nil) :config (setq evil-collection-mode-list (remove 'lispy evil-collection-mode-list)) (evil-collection-init))
Evil-Org-Mode is an extension of Evil into Org-Mode.
;; Use evil-org for evil extensions to org-mode (use-package evil-org :after org :hook ((org-mode . evil-org-mode) (org-agenda-mode . evil-org-mode) (evil-org-mode . (lambda () (evil-org-set-key-theme '(navigation todo insert textobjects additional))))) :config (require 'evil-org-agenda) (evil-org-agenda-set-keys)) ;; Add to leader key bindings (jdw/leader-key-def "o" '(:ignore t :which-key "org mode") "oi" '(:ignore t :which-key "insert") "oil" '(org-insert-link :which-key "insert link") "on" '(org-toggle-narrow-to-subtree :which-key "toggle narrow") "oa" '(org-agenda :which-key "status") "ot" '(org-todo-list :which-key "todos") "oc" '(org-capture t :which-key "capture") "ox" '(org-export-dispatch t :which-key "export"))
¶Which Key
which-key is a useful UI panel that appears when you start pressing any key binding in Emacs to offer you all possible completions for the prefix. For example, if you press C-c
(hold control and press the letter c
), a panel will appear at the bottom of the frame displaying all of the bindings under that prefix and which command they run. This is very useful for learning the possible key bindings in the mode of your current buffer.
(use-package which-key :defer 0 :diminish which-key-mode :config (which-key-mode) (setq which-key-idle-delay 1))
¶Completion System
- Veritico Completions
vertico is a minimalist vertical completion interface that plays well with other packages.
(defun jdw/minibuffer-backward-kill (arg) "When minibuffer is completing a file name delete up to parent folder, otherwise delete a word" (interactive "p") (if minibuffer-completing-file-name ;; Borrowed from https://github.com/raxod502/selectrum/issues/498#issuecomment-803283608 (if (string-match-p "/." (minibuffer-contents)) (zap-up-to-char (- arg) ?/) (delete-minibuffer-contents)) (backward-kill-word arg))) ;; Enable vertico for completions (use-package vertico :bind (:map vertico-map ("C-j" . vertico-next) ("C-k" . vertico-previous) ("C-f" . vertico-exit) :map minibuffer-local-map ("M-h" . jdw/minibuffer-backward-kill)) :custom (vertico-cycle t) :init (vertico-mode))
- Save Mini-Buffer History
Use the internal
savehist
package to presever the mini-buffer history.;; Preserve minibuffer history with savehist (use-package savehist :config (setq history-length 25) (savehist-mode 1))
- Orderless Candidate Matching
orderless enables space separated candidate matching for all components of the completions.
;; Enable orderless for completion style (use-package orderless :init (setq completion-styles '(orderless) completion-category-defaults nil completion-category-overrides '((file (styles partial-completion)))))
- Marginalia Mini-Buffer Annotations
marginalia enables completion annotations in the minibuffer.
;; Enable completion annotations with marginalia (use-package marginalia :after vertico :custom (marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)) :init (marginalia-mode))
- Corfu Region Completion
corfu enhances completion at point in a minimalist approach.
;; Enhance completion at point with corfu (use-package corfu :bind (:map corfu-map ("C-j" . corfu-next) ("C-k" . corfu-previous) ("C-f" . corfu-insert)) :custom (corfu-cycle t) :config (global-corfu-mode))
- Search and Navigation with Consult
consult provides minimal search and navigation commands.
;; Use consult for search/navigation (use-package consult :demand t :bind (("C-s" . consult-line) ("C-M-l" . consult-imenu) :map minibuffer-local-map ("C-r" . consult-history)) :custom (completion-in-region-function #'consult-completion-in-region))
- Completion Actions with Embark
Embark allows completion actions, among other things. There’s a System Crafters video to get started, and he mentions some other good resources as well:
- Embark README
- Karthik’s great blog post
- Additional action on the Embark wiki
- Additional config on the Embark wiki
- Prot’s Embark video
;; Use Embark for completion actions (use-package embark :bind (("C-S-a" . embark-act) :map minibuffer-local-map ("C-d" . embark-act)) :config ;; Show Embark actions via which-key (setq embark-action-indicator (lambda (map) (which-key--show-keymap "Embark" map nil nil 'no-paging) #'which-key--hide-popup-ignore-command) embark-become-indicator embark-action-indicator)) ;; Use embark-consult for consult integration (use-package embark-consult)
¶Auto-Save Changed Files
super-save automatically auto-saves changed files when certain events occur, such as buffer changes.
(use-package super-save :defer 1 :diminish super-save-mode :config (super-save-mode +1) (setq super-save-auto-save-when-idle t))
¶Highlight Keywords
hl-todo is a useful tool to highlight keywords like TODO or BUG.
;; Use hl-todo to highlight keywords (use-package hl-todo :init (global-hl-todo-mode))
¶Rainbow Delimiters
rainbow-delimiters is useful in programming modes because it colorizes nested parentheses and brackets according to their nesting depth. This makes it a lot easier to visually match parentheses in Emacs Lisp code without having to count them yourself.
;; Use rainbow colors for things like parentheses and brackets (use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))
¶Rainbow Mode
Rainbow Mode enables visualization of color codes like #BD93F9 in Emacs.
;; Use rainbow mode to see color codes highlighted (use-package rainbow-mode :hook prog-mode org-mode)
¶Flycheck Syntax Checking
Flycheck provides on the fly syntax checking.
(use-package flycheck :defer t :hook (lsp-mode . flycheck-mode))
¶Org Mode
Org Mode is one of the hallmark features of Emacs. It is a rich document editor, project planner, task and time tracker, blogging engine, and literate coding utility all wrapped up in one package.
¶Better Font Faces
The jdw/org-font-setup
function configures various text faces to tweak the sizes of headings and use variable width fonts in most cases so that it looks more like we’re editing a document in org-mode
. We switch back to fixed width (monospace) fonts for code blocks and tables so that they display correctly.
;; Function to call for specifying org-mode fonts (defun jdw/org-font-setup () ;; Replace list hyphen with dot (font-lock-add-keywords 'org-mode '(("^ *\\([-]\\) " (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•")))))) ;; Set faces for heading levels (dolist (face '((org-level-1 . 1.1) (org-level-2 . 1.1) (org-level-3 . 1.1) (org-level-4 . 1.1) (org-level-5 . 1.1) (org-level-6 . 1.1) (org-level-7 . 1.1) (org-level-8 . 1.1))) (set-face-attribute (car face) nil :font "Fira Sans" :weight 'regular :height (cdr face))) ;; Ensure that anything that should be fixed-pitch in Org files appears that way (set-face-attribute 'org-block nil :foreground nil :inherit 'fixed-pitch) (set-face-attribute 'org-table nil :inherit 'fixed-pitch) (set-face-attribute 'org-formula nil :inherit 'fixed-pitch) (set-face-attribute 'org-code nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-table nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch)) (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch)) (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch)) (set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch) (set-face-attribute 'line-number nil :inherit 'fixed-pitch) (set-face-attribute 'line-number-current-line nil :inherit 'fixed-pitch) ;; Change LaTeX font size (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)))
¶Basic Config
This section contains the basic configuration for org-mode
.
;; Function for basic org-mode setup (defun jdw/org-mode-setup () (org-indent-mode) (variable-pitch-mode 1) (visual-line-mode 1)) ;; Load the org package (use-package org ;;:pin org :commands (org-capture org-agenda) :hook (org-mode . jdw/org-mode-setup) :bind (("C-c a" . org-agenda)) :config ;; Hide emphasis markers on formatted text (setq org-hide-emphasis-markers t) ;; Specify elipsis symbol (setq org-ellipsis " ▾") ;; Change org-mode logging (setq org-agenda-start-with-log-mode t) (setq org-log-done 'time) (setq org-log-into-drawer t) ;; Specify files to build org-agenda (setq org-agenda-files '("~/Documents/Org/inbox.org" "~/Documents/Org/todo.org")) ;; Track habits with org-habit (require 'org-habit) (add-to-list 'org-modules 'org-habit) (setq org-habit-graph-column 60) ;; Customize todo keywords (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "EVENT(e)" "|" "DONE(d!)"))) ;; Customize tags (setq org-tag-alist '((:startgroup) ; Put mutually exclusive tags here (:endgroup) ("@home" . ?H) ("@work" . ?W) ("errand" . ?e) ("agenda" . ?a) ("chore" .?c) ("idea" . ?i))) ;; Place org agenda tags column (setq org-agenda-tags-column 0) ;; Only one space after a tag (setq org-tags-column 0) ;; Set up org-mode fonts (jdw/org-font-setup) ;; Evil implementiation (evil-define-key '(normal insert visual) org-mode-map (kbd "C-j") 'org-next-visible-heading) (evil-define-key '(normal insert visual) org-mode-map (kbd "C-k") 'org-previous-visible-heading) (evil-define-key '(normal insert visual) org-mode-map (kbd "M-j") 'org-metadown) (evil-define-key '(normal insert visual) org-mode-map (kbd "M-k") 'org-metaup))
¶Nicer Heading Bullets
org-bullets replaces the heading stars in org-mode
buffers with nicer looking characters that you can control.
;; Use nicer looking bullets for org-mode (use-package org-bullets :hook (org-mode . org-bullets-mode) :config (setq org-hide-leading-stars t) :custom (org-bullets-bullet-list '("◉" "○" "●" "○" "●" "○" "●")))
¶Center Org Buffers
We use visual-fill-column to center org-mode
buffers for a more pleasing writing experience as it centers the contents of the buffer horizontally to seem more like you are editing a document. This is really a matter of personal preference so you can remove the block below if you don’t like the behavior.
;; Specify visual-fill centering settings (defun jdw/org-mode-visual-fill () (setq visual-fill-column-width 120 visual-fill-column-center-text t) (visual-fill-column-mode 1)) (defun jdw/dashboard-mode-visual-fill () (setq visual-fill-column-width 100 visual-fill-column-center-text t) (visual-fill-column-mode 1)) ;; Use visual-fill-column to center org-mode buffers (use-package visual-fill-column :hook ((org-mode . jdw/org-mode-visual-fill) (dashboard-mode . jdw/dashboard-mode-visual-fill)))
¶Configure Babel Languages
To execute or export code in org-mode
code blocks, you’ll need to set up org-babel-load-languages
for each language you’d like to use. This page documents all of the languages that you can use with org-babel
.
;; Specify org-babel languages (with-eval-after-load 'org (org-babel-do-load-languages 'org-babel-load-languages '((emacs-lisp . t) (python . t) (R . t) (lua . t) (shell . t))) (push '("conf-unix" . conf-unix) org-src-lang-modes))
¶Structure Templates
Org Mode’s structure templates feature enables you to quickly insert code blocks into your Org files in combination with org-tempo
by typing <
followed by the template name like el
or py
and then press TAB
. For example, to insert an empty emacs-lisp
block below, you can type <el
and press TAB
to expand into such a block.
You can add more src
block templates below by copying one of the lines and changing the two strings at the end, the first to be the template name and the second to contain the name of the language as it is known by Org Babel.
;; Apply structure templates to quickly insert code blocks in org files (with-eval-after-load 'org ;; This is needed as of Org 9.2 (require 'org-tempo) ;; Specify structure templates (add-to-list 'org-structure-template-alist '("sh" . "src shell")) (add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp")) (add-to-list 'org-structure-template-alist '("py" . "src python")) (add-to-list 'org-structure-template-alist '("r" . "src R")) (add-to-list 'org-structure-template-alist '("lua" . "src lua")))
¶Auto-tangle Configuration Files
This snippet adds a hook to org-mode
buffers so that jdw/org-babel-tangle-config
gets executed each time such a buffer gets saved. This function checks to see if the file being saved is in the directory ~/.dotfiles/
, and if so, tangles the file to the file path specified in the header arguments for the code block to tangle.
;; Automatically tangle our Emacs.org config file when we save it (defun jdw/org-babel-tangle-config () ;; Check when the buffer file is in my dot-file directory (when (string-equal (file-name-directory (buffer-file-name)) (expand-file-name "~/.dotfiles/")) ;; Dynamic scoping to the rescue (let ((org-confirm-babel-evaluate nil)) (org-babel-tangle)))) ;; Run the function after saving (add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'jdw/org-babel-tangle-config)))
¶Org-Roam
Org Roam is an Org Mode extension inspired by Roam and the Zettelkasten note-taking approach. I particularly like it because it solves the problem of organizing Org files, which has completely stopped several projects or throughts in their tracks before. With this approach, you just make the file, get your thoughts out there, and then move on.
;; Set up org-roam (use-package org-roam :ensure t :custom (org-roam-directory "~/Documents/Org/OrgRoam") :bind (("C-c n l" . org-roam-buffer-toggle) ("C-c n f" . org-roam-node-find) ("C-c n i" . org-roam-node-insert)) :config (org-roam-setup)) ;; Org-Roam keys (jdw/leader-key-def "n" '(:ignore t :which-key "org-roam") "nl" '(org-roam-buffer-toggle :which-key "toggle buffer") "nf" '(org-roam-node-find :which-key "find node") "ni" '(org-roam-node-insert :which-key "insert node"))
- Org-Roam-UI
Org-Roam-UI is a graphical front-end showing linkages for the Org-Roam files you’ve made.
;; Load websocket, a dependency for Org-Roam-UI (use-package websocket :after org-roam) ;; Load and configure Org-Roam-UI (use-package org-roam-ui :after org-roam :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-Roam keys (jdw/leader-key-def "nu" '(org-roam-ui-open :which-key "open org-roam-ui"))
¶Development
¶Manage Projects with Projectile
Projectile is a project interaction library for things like finding project files and navigating through projects.
;; Enable Projectile for project interactions (use-package projectile ;; Hide minor mode string in the mode-line :diminish projectile-mode :config (projectile-mode) :demand t :bind-keymap ("C-c p" . projectile-command-map))
Consult-Projectile incorporates Consult into Projectile.
(use-package consult-projectile :after projectile :bind (("C-M-p" . consult-projectile-find-file)))
¶Commenting Lines
Evil Nerd Commenter allows for commenting that acts more like I’m used to from traditional IDEs, and a bit more.
(use-package evil-nerd-commenter :bind ("M-/" . evilnc-comment-or-uncomment-lines))
¶IDE Features with lsp-mode
- lsp-mode
We use the excellent lsp-mode to enable IDE-like functionality for many different programming languages via “language servers” that speak the Language Server Protocol. Before trying to set up
lsp-mode
for a particular language, check out the documentation for your language so that you can learn which language servers are available and how to install them.The
lsp-keymap-prefix
setting enables you to define a prefix for wherelsp-mode
’s default keybindings will be added. I highly recommend using the prefix to find out what you can do withlsp-mode
in a buffer.The
which-key
integration adds helpful descriptions of the various keys so you should be able to learn a lot just by pressingC-c l
in alsp-mode
buffer and trying different things that you find there.One of the dependencies of lsp-mode is Spinner.el, but straight is currently looking in the wrong place for it. Let’s explicitly install it before getting started.
;; Install spinner, a dependency for lsp-mode (use-package spinner :straight '(spinner :type git :host github :repo "Malabarba/spinner.el" :files (:defaults)))
Now we can set up
lsp-mode
.;; Build the breadcrumbs in LSP mode (defun jdw/lsp-mode-setup () (setq lsp-headerline-breadcrumb-segments '(path-up-to-project file symbols)) (lsp-headerline-breadcrumb-mode)) ;; Enable LSP mode (use-package lsp-mode ;; Don't auto-load the package until we run these commands :commands (lsp lsp-deferred) ;; Run our breadcrumbs function in LSP instances :hook (lsp-mode . jdw/lsp-mode-setup) :init (setq lsp-keymap-prefix "C-c l") :config (lsp-enable-which-key-integration t))
- lsp-ui
lsp-ui is a set of UI enhancements built on top of
lsp-mode
which make Emacs feel even more like an IDE. Check out the screenshots on thelsp-ui
homepage (linked at the beginning of this paragraph) to see examples of what it can do.;; Enable LSP-mode UI enhancements (use-package lsp-ui :hook (lsp-mode . lsp-ui-mode) :custom (lsp-ui-doc-position 'bottom))
- lsp-treemacs
lsp-treemacs provides nice tree views for different aspects of your code like symbols in a file, references of a symbol, or diagnostic messages (errors and warnings) that are found in your code.
Try these commands with
M-x
:lsp-treemacs-symbols
- Show a tree view of the symbols in the current filelsp-treemacs-references
- Show a tree view for the references of the symbol under the cursorlsp-treemacs-error-list
- Show a tree view for the diagnostic messages in the project
This package is built on the treemacs package which might be of some interest to you if you like to have a file browser at the left side of your screen in your editor.
(use-package lsp-treemacs :after lsp)
¶Debugging with dap-mode
dap-mode is an excellent package for bringing rich debugging capabilities to Emacs via the Debug Adapter Protocol. You should check out the configuration docs to learn how to configure the debugger for your language. Also make sure to check out the documentation for the debug adapter to see what configuration parameters are available to use for your debug templates!
(use-package dap-mode ;;:custom ;;(lsp-enable-dap-auto-configure nil) :config ;;(dap-ui-mode 1) ;;(dap-tooltip-mode 1) (require 'dap-node) (dap-node-setup))
¶Languages
- C/C++
(use-package ccls :hook ((c-mode c++-mode objc-mode cuda-mode) . (lambda () (require 'ccls) (lsp))))
- Emacs Lisp
(add-hook 'emacs-lisp-mode-hook #'flycheck-mode) (use-package helpful :custom (counsel-describe-function-function #'helpful-callable) (counsel-describe-variable-function #'helpful-variable) :bind ([remap describe-function] . helpful-function) ([remap describe-symbol] . helpful-symbol) ([remap describe-variable] . helpful-variable) ([remap describe-command] . helpful-command) ([remap describe-key] . helpful-key)) (jdw/leader-key-def "e" '(:ignore t :which-key "eval") "eb" '(eval-buffer :which-key "eval buffer")) (jdw/leader-key-def :keymaps '(visual) "er" '(eval-region :which-key "eval region"))
- Lua
;; Use the Lua major mode for editing Lua code (use-package lua-mode :defer 1 :config ;; Better indenting, plus avoided double indents (setq lua-indent-nested-block-content-align nil) (setq lua-indent-close-paren-align nil) (defun lua-at-most-one-indent (old-function &rest arguments) (let ((old-res (apply old-function arguments))) (if (> old-res lua-indent-level) lua-indent-level old-res))) (advice-add #'lua-calculate-indentation-block-modifier :around #'lua-at-most-one-indent))
- Python
We use
lsp-mode
anddap-mode
to provide a more complete development environment for Python in Emacs.Make sure you have the
pylsp
language server installed before tryinglsp-mode
!pip install --user "python-lsp-server[all]"
There are a number of other language servers for Python so if you find that
pylsp
doesn’t work for you, consult thelsp-mode
language configuration documentation to try the others!;; Use the Python major mode for editing code (use-package python-mode :hook (python-mode . lsp-deferred) :custom (dap-python-debugger 'debugpy) :config (require 'dap-python))
- R
Emacs Speaks Statistics (ESS) is a great add-on built for handling a lot of statistical programs such as R, SAS, and Stata.
;; Use the Emacs Speaks Statistics package (use-package ess :init (require 'ess-site))
- Fortran
f90-mode
is already a mode in base Emacs, but we still need to install the language server.pip install --user "fortls"
¶Anaconda Integration
I use Miniconda for my Python and R environment and package management. To integrate it with Emacs, we can use the conda package.
;; Configure conda package for anaconda integration (use-package conda :init (setq conda-anaconda-home (expand-file-name "~/.miniconda")) (setq conda-env-home-directory (expand-file-name "~/.miniconda/")))
¶Magit
Magit is the best Git interface I’ve ever used. Common Git operations are easy to execute quickly using Magit’s command panel system.
;; Configure magit for git integration (use-package magit) ;; Add leader key bindings (jdw/leader-key-def "g" '(:ignore t :which-key "git") "gs" 'magit-status "gd" 'magit-diff-unstaged "gc" 'magit-branch-or-checkout "gl" '(:ignore t :which-key "log") "glc" 'magit-log-current "glf" 'magit-log-buffer-file "gb" 'magit-branch "gP" 'magit-push-current "gp" 'magit-pull-branch "gf" 'magit-fetch "gF" 'magit-fetch-all "gr" 'magit-rebase)
¶Git Gutter
Git Gutter helps by highlighting changes to the branch.
(use-package git-gutter :hook ((text-mode . git-gutter-mode) (prog-mode . git-gutter-mode)) :config (setq git-gutter:update-interval 0.02)) (use-package git-gutter-fringe :config (define-fringe-bitmap 'git-gutter-fr:added [224] nil nil '(center repeated)) (define-fringe-bitmap 'git-gutter-fr:modified [224] nil nil '(center repeated)) (define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240] nil nil 'bottom))