Emacs Setup for macOS and GNU/Linux

Table of Contents

This file is a literate program for my emacs configuration. It details how to get started with my setup on macOS and GNU/Linux, which includes:

This file is also exported as a Github pages site here. The repo is here.

emacs_icon.png

1. Base Installation

This section goes over the base installation procedure for vanilla emacs and LaTeX.

1.1. macOS

First, install command line developer tools and homebrew if it is not already installed.

xcode-select --install
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

At this point, brew should be installed, but it may not be added to your path yet. You should run the following two commands to add it to your PATH or follow the instructions that are at the end of the homebrew installation.

(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"

Next, install emacs-plus. Check the link for more compilation options in case you need different features.

brew tap d12frosted/emacs-plus
brew install emacs-plus@30 --with-ctags --with-xwidgets --with-imagemagick --with-native-comp --with-poll --with-modern-alecive-flatwoken-icon

At this step, I also recommend installing basictex for pdflatex support.

brew install --cask basictex
eval "$(/usr/libexec/path_helper)"

To install packages during compilation failures, please refer to CTAN and install packages using tlmgr.

sudo tlmgr update --self
sudo tlmgr install wrapfig capt-of ulem etoolbox listings

1.2. GNU/Linux

Before installing emacs, first install some dependencies using your package manager. In particular, we need imagemagick for pdf/image support and libgccjit for native compilation. Below I have instructions for pacman, the Arch package manager:

sudo pacman -S imagemagick libgccjit

I recommend building emacs from source to take advantage of all the features of emacs, like xwidgets, imagemagick, and native compilation support. Instructions from the official emacs git repo.

cd ~
git clone https://git.savannah.gnu.org/git/emacs.git
cd emacs
./autogen.sh
./configure --with-xwidgets --with-imagemagick --with-native-compilation
make -j
sudo make install

If webkit does not work, you may need to set export WEBKIT_DISABLE_COMPOSITING_MODE=1 in your ~/.bashrc to load webpages instead of showing a white screen.

At this step, I also recommend installing a minimal version of TeX live following these instructions:

cd /tmp
wget https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz
zcat < install-tl-unx.tar.gz | tar xf -
cd install-tl-*
sudo perl ./install-tl --no-interaction --scheme=basic

You must add texlive to the path by editing your .bashrc file in the home directory by running the following command (assuming you are using the bash shell):

sudo bash -c '(echo; echo "export PATH=\$PATH:/usr/local/texlive/2024/bin/x86_64-linux") >> /etc/profile'

To allow the user to run texlive commands (like tlmgr and pdflatex), run sudo chown -R [username] /usr/local/texlive which gives ownership to your user.

To install packages during compilation failures, please refer to CTAN and install packages using tlmgr.

tlmgr update --self
tlmgr install wrapfig capt-of

2. Conda Setup

To install conda, I used homebrew's miniconda cask for macos:

brew install --cask miniconda
conda init "$(basename "${SHELL}")"

On GNU/Linux, I used mambaforge directly:

wget "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh

After installing conda, remember to close and re-open your terminal. When you re-open it, you should be able to see (base) before your command line prompt, which confirms that miniconda is active.

I would also recommend creating a test environment in madrona, as follows:

conda create -n testenv python=3.10

You will be able to activate and deactive this on the terminal, and integrate this environment (along with new and existing conda environments) into emacs.r

To use virtual environments, first ensure that there is a symbolic link from the directory to the ~/.virtualenvs directory. Since I installed miniconda through homebrew, I needed to run the following script first for macos.

ln -s /opt/homebrew/Caskroom/miniconda/base/envs ~/.virtualenvs

On GNU/Linux, you can link mambaforge directly instead:

ln -s ~/miniforge3/envs ~/.virtualenvs

3. Install this Config

If you have existing configuration files, I strongly recommend creating a backup of your .emacs.d directory. For instance, I just called mv ~/.emacs.d ~/emacs_old to save my old configuration.

Next, clone this repo and set up symbolic links so changes to this repo are reflected in the emacs init file.

git clone https://github.com/bsarkar321/emacs_setup
cd emacs_setup

mkdir ~/.emacs.d

ln -s "$(pwd)"/early-init.el ~/.emacs.d/early-init.el
ln -s "$(pwd)"/init.el ~/.emacs.d/init.el

After this point, your emacs should be fully set up using my configuration! You can launch emacs and open this file from the terminal by calling emacs README.org. Note that packages will be installed the first time you set up, and the config may not be fully loaded. At this point, it is normal to see many compilation messages and warnings. However, if you close emacs and open it up again, you should see my full setup loaded.

When opening README.org you may see the following warning:

The local variables list in README.org
or .dir-locals.el contains values that may not be safe (*).

Do you want to apply it?  You can type
y  -- to apply the local variables list.
n  -- to ignore the local variables list.
!  -- to apply the local variables list, and permanently mark these
      values (*) as safe (in the future, they will be set automatically.)
i  -- to ignore the local variables list, and permanently mark these
      values (*) as ignored

  * eval : (add-hook 'after-save-hook (lambda nil (when (y-or-n-p "Tangle?") (org-babel-tangle) (load-file user-init-file) (org-html-export-to-html))) nil t)

This is a safety feature to prevent arbitrary code execution in emacs. If you click "y" or "!" you will enable the feature of this file to automatically reload the configuration whenever you save.

4. Tips for Using Emacs

[WIP]

The rest of this file directly contains the code for the early-init and init files. You can edit this document directly, and the changes will be applied upon saving in emacs (if you accept the question that pop up in the minibuffer).

5. Early Init

This early init uses some tricks from Doom Emacs to reduce startup time. It also changes the default frame at startup, which is faster than changing it after the frame is initialized. Specifically:

  • tool-bar-lines (the top bar with gui icons) is disabled
  • vertical-scroll-bars are disabled
  • the title bar is transparent (has same color as rest of emacs)
  • The width and height are half the screen by default
 1: ;;; early-init.el --- Early initialization
 2: 
 3: ;;; Commentary:
 4: ;;
 5: ;; Using early-init for speed.
 6: ;;
 7: 
 8: ;;; Code:
 9: 
10: (setq gc-cons-threshold most-positive-fixnum
11:         gc-cons-percentage 0.6)
12: 
13: (add-hook 'emacs-startup-hook
14:             (lambda ()
15:               (setq gc-cons-threshold 16777216
16:                     gc-cons-percentage 0.1)))
17: 
18: (defun doom-defer-garbage-collection-h ()
19:   "Defer gc."
20:   (setq gc-cons-threshold most-positive-fixnum))
21: 
22: (defun doom-restore-garbage-collection-h ()
23:   "Restore gc."
24:   (run-at-time
25:    1 nil (lambda () (setq gc-cons-threshold 16777216))))
26: 
27: (add-hook 'minibuffer-setup-hook #'doom-defer-garbage-collection-h)
28: (add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h)
29: 
30: ;; package-initialize already in init.el
31: (setq package-enable-at-startup nil)
32: 
33: ;; Inhibit resizing frame
34: (setq frame-inhibit-implied-resize t)
35: 
36: ;; Disable before actual init
37: (push '(tool-bar-lines . 0) default-frame-alist)
38: (push '(vertical-scroll-bars) default-frame-alist)
39: (when (featurep 'ns)
40:   (push '(ns-transparent-titlebar . t) default-frame-alist))
41: (push '(width . 0.5) default-frame-alist)
42: (push '(height . 0.5) default-frame-alist)
43: 
44: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
45: ;;; early-init.el ends here

6. Init

The code below gets constructed into the init.el, where most of the customization lies.

 1: ;; .emacs.d/init.el --- Init for Emacs
 2: 
 3: ;;; Commentary:
 4: 
 5: ;; Lightweight Emacs config file to make Emacs look like atom
 6: ;; while having tramp/python/c++/latex support.
 7: 
 8: ;;; Code:
 9: 
10: ;; This is only here to resolve flymake complaints
11: (eval-when-compile
12:   (defvar xwwp-search-prefix)
13:   (defvar org-image-actual-width)
14:   (defvar org-support-shift-select)
15:   (defvar org-latex-listings)
16:   (defvar org-src-fontify-natively)
17:   (defvar org-file-apps)
18:   (defvar TeX-auto-save)
19:   (defvar TeX-parse-self)
20:   (defvar TeX-view-program-selection)
21:   (defvar TeX-source-correlate-start-server)
22:   (defvar TeX-source-correlate-method)
23:   (defvar TeX-source-correlate-mode)
24:   (defvar reftex-plug-into-AUCTeX)
25:   (defvar reftex-bibliography-commands)
26: 
27:   (defvar markdown-command)
28:   (defvar LaTeX-mode-map)
29:   (defvar corfu-map)
30:   (defvar corfu-popupinfo-delay)
31:   (defvar tramp-use-ssh-controlmaster-options)
32:   (defvar tramp-verbose)
33: 
34:   (declare-function pdf-loader-install nil)
35:   (declare-function TeX-revert-document-buffer nil)
36:   (declare-function pyvenv-mode nil)
37:   (declare-function pyvenv-workon-home nil)
38:   (declare-function global-corfu-mode nil)
39:   (declare-function corfu-popupinfo-mode nil)
40:   )
41: 

6.1. Package Support

Here I define that melpa should be added to the package archives, which has a larger repository of user-created packages.

42: ;; ===================================
43: ;; MELPA Package Support
44: ;; ===================================
45: ;; Enables basic packing support
46: (require 'package)
47: 
48: (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
49: 
50: (package-initialize)
51: 
52: (when (not package-archive-contents) (package-refresh-contents))
53: 
54: (defvar my-packages
55:   '(atom-one-dark-theme  ;; melpa
56:     corfu
57:     jupyter              ;; melpa
58:     htmlize
59:     magit
60:     markdown-mode
61:     pdf-tools
62:     pyvenv               ;; melpa
63:     use-package
64:     xwwp                 ;; melpa
65:     processing-mode      ;; melpa
66:     ))
67: 
68: ;; Iterate on packages and install missing ones
69: (dolist (pkg my-packages)
70:   (unless (package-installed-p pkg)
71:     (package-install pkg)))
72: 

6.2. Basic Customization

Adding basic customization options, like loading the theme and setting a custom-file so it does not conflict with this init file.

73: ;; ===================================
74: ;; Basic Customization
75: ;; ===================================
76: 
77: (load-theme 'atom-one-dark t)
78: 
79: (setq inhibit-startup-message t)    ;; Hide the startup message
80: (setq column-number-mode t)         ;; Show column number in mode-line
81: (global-superword-mode t)           ;; Superword for all buffers
82: (delete-selection-mode t)           ;; Delete when typing over selection
83: (setq split-height-threshold nil)   ;; Do not split by height by default
84: (setq split-width-threshold 0)      ;; Split by width by default
85: (electric-pair-mode t)              ;; Default electric pairs (opening parentheses create closing)
86: (setq use-short-answers t)          ;; Use y/n instead of yes or no
87: (setq make-backup-files nil)        ;; Do not save the backup files ~filename
88: 
89: (windmove-default-keybindings 'meta);; Use option key with arrows to switch which window is active
90: 
91: ;; separate custom.el so users can set preferences separately
92: (defconst custom-file (expand-file-name "custom.el" user-emacs-directory))
93: (load custom-file t)
94: 

6.3. macOS Support

Enables some mac-specific customization, including full screen with command-control-f, and commenting/uncommenting regions of code with command-/.

I also use command-option-<arrow-keys> to change the size of the focused window.

 95: ;; ===================================
 96: ;; macOS Default Keys
 97: ;; ===================================
 98: (setq mac-option-modifier 'meta
 99:         mac-command-modifier 'super
100:         mac-right-option-modifier 'meta)
101: 
102: (when (string= system-type "darwin") (defvar dired-use-ls-dired nil))
103: 
104: (defun comment-or-uncomment-region-or-line ()
105:   "Comments the region or the current line if there's no active region."
106:   (interactive)
107:   (let (beg end)
108:     (if (region-active-p)
109:           (setq beg (region-beginning) end (region-end))
110:         (setq beg (line-beginning-position) end (line-end-position)))
111:     (comment-or-uncomment-region beg end)))
112: 
113: (global-set-key (kbd "s-Z") 'undo-redo)
114: (global-set-key (kbd "s-/") 'comment-or-uncomment-region-or-line)
115: (global-set-key (kbd "C-s-f") 'toggle-frame-fullscreen)
116: 
117: (global-set-key (kbd "M-s-<left>") 'shrink-window-horizontally)
118: (global-set-key (kbd "M-s-<right>") 'enlarge-window-horizontally)
119: (global-set-key (kbd "M-s-<up>") 'shrink-window)
120: (global-set-key (kbd "M-s-<down>") 'enlarge-window)
121: 

6.4. Xwidgets Convenience functions

Use xwwp to have a browser within emacs.

Note: it is currently a big buggy, especially when multiple windows are created. However it works great for previewing html or testing out websites.

122: ;; ===================================
123: ;; xwwp setup
124: ;; ===================================
125: (use-package xwwp)
126: (setq xwwp-search-prefix "https://duckduckgo.com/?q=")
127: 

6.5. Writing

Enable line numbers, word wrap, and spell checking for text modes.

128: ;; ===================================
129: ;; Writing Modes
130: ;; ===================================
131: (defun human-text-on ()
132:   "Turn on human text options."
133:   (turn-on-visual-line-mode)
134:   (display-line-numbers-mode)
135:   )
136: 
137: ;; Txt support
138: (add-hook 'text-mode-hook #'human-text-on)
139: 

6.5.1. Org Mode

Writing for org mode documents, like this configuration file! Enables opening web links within emacs, along with some other customization options to make org mode easier for me to work with.

140: ;; Org support
141: (setq org-image-actual-width 500)
142: (add-hook 'org-mode-hook #'human-text-on)
143: (setq org-support-shift-select t)
144: (setq org-latex-src-block-backend 'listings)
145: 
146: (use-package htmlize)
147: (setq org-src-fontify-natively t)
148: (setq browse-url-browser-function 'xwidget-webkit-browse-url)
149: (setq org-file-apps
150:         '((auto-mode . emacs)
151:           ("\\.x?html?\\'" . (lambda (file link) (xwidget-webkit-browse-url (concat "file://" link))))
152:           ("\\.mp4\\'" . "vlc \"%s\"")))
153: 
154: (org-babel-do-load-languages 'org-babel-load-languages
155:                                '(
156:                                  (python . t)
157:                                  (shell . t)
158:                                  ))
159: 

6.5.2. Markdown Mode

Write markdown documents, common for project README files.

160: ;; Markdown support
161: (use-package markdown-mode
162:   :ensure t
163:   :mode ("README\\.md\\'" . gfm-mode)
164:   :init (setq markdown-command "multimarkdown"))
165: 

6.5.3. LaTeX

Write LaTeX documents with support for previewing and syncing (like double clicking on overleaf previews).

166: ;; Latex support
167: (pdf-loader-install)
168: (add-hook 'pdf-view-mode-hook 'pdf-view-dark-minor-mode)
169: 
170: (add-hook 'eww-mode-hook 'pdf-tools-install)
171: 
172: (use-package tex
173:   :ensure auctex)
174: 
175: (add-hook 'LaTeX-mode-hook
176:             (lambda()
177:               (turn-on-reftex)
178:               (flyspell-mode)
179:               (setq TeX-auto-save t)
180:               (setq TeX-parse-self t)
181:               (setq TeX-view-program-selection '((output-pdf "PDF Tools"))
182:                     TeX-source-correlate-start-server t)
183:               (setq TeX-source-correlate-method 'synctex)
184: 
185:               (setq TeX-source-correlate-mode t)
186:               (setq-default TeX-master nil)
187:               (global-set-key (kbd "C-c C-g") 'pdf-sync-forward-search)
188:               (setq reftex-plug-into-AUCTeX t)
189:               (setq reftex-bibliography-commands '("bibliography" "nobibliography" "addbibresource"))
190:               (define-key LaTeX-mode-map (kbd "$") 'self-insert-command)
191:               )
192:             )
193: 
194: (add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
195: 

6.6. Programming

Work with programming modes, even in remote systems using TRAMP.

196: ;; ===================================
197: ;; Programming Modes
198: ;; ===================================
199: (add-hook 'prog-mode-hook 'display-line-numbers-mode) ;; Show line numbers for programming languages
200: 
201: (use-package corfu
202:   :custom
203:   ;; (corfu-cycle t)                ;; Enable cycling for `corfu-next/previous'
204:   (corfu-auto t)                 ;; Enable auto completion
205:   ;; (corfu-separator ?\s)          ;; Orderless field separator
206:   ;; (corfu-quit-at-boundary nil)   ;; Never quit at completion boundary
207:   ;; (corfu-quit-no-match nil)      ;; Never quit, even if there is no match
208:   ;; (corfu-preview-current nil)    ;; Disable current candidate preview
209:   ;; (corfu-preselect 'prompt)      ;; Preselect the prompt
210:   ;; (corfu-on-exact-match nil)     ;; Configure handling of exact matches
211:   ;; (corfu-scroll-margin 5)        ;; Use scroll margin
212: 
213:   :bind
214:   (:map corfu-map
215:         ;; Option 1: Unbind RET completely
216:         ("RET" . nil))
217:   :init
218:   (global-corfu-mode))
219: 
220: (use-package corfu-popupinfo
221:   :init
222:   (corfu-popupinfo-mode)
223:   (setq corfu-popupinfo-delay '(1.0 . 0.1)))
224: 
225: (use-package emacs
226:   :init
227:   (setq completion-cycle-threshold 3))
228: 

6.6.1. Elisp Support

Elisp syntax checking for emacs configuration files.

229: (add-hook 'emacs-lisp-mode-hook 'flymake-mode)
230: 

6.6.2. Python Support

Python with eglot as an LSP. Note that you may need to pip install a language server after activating a virtual environment.

231: ;; Python with eglot
232: ;; (add-hook 'python-mode-hook 'eglot-ensure)
233: (setq-default eglot-workspace-configuration
234:               '(:pylsp (:skip_token_initialization t
235:                         :plugins (:ruff (:enabled t
236:                                                   :formatEnabled t)
237:                                   :pylsp_mypy (:enabled t)))
238:                 ))
239: 

6.6.3. Tramp + Conda

Connecting virtual environments on local and remote systems.

240: ;; Conda and TRAMP setup
241: 
242: (pyvenv-mode 1)
243: 
244: (defun tramp-conda-setup()
245:   "Set up conda for tramp."
246:   (when default-directory (if (file-remote-p default-directory)
247:                                 (setenv "WORKON_HOME" (concat (file-remote-p default-directory) "~/.virtualenvs"))
248:                               (setenv "WORKON_HOME" "~/.virtualenvs"))))
249: 
250: (advice-add #'pyvenv-workon-home :before #'tramp-conda-setup)
251: 
252: (setq tramp-use-ssh-controlmaster-options nil)
253: (setq exec-path (append exec-path '("/afs/.ir/users/b/i/bidiptas/bin")))
254: (setq tramp-verbose 6)
255: 

6.6.4. C/C++/CUDA

Add support for CUDA and C/C++. When using cmake remember to do cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 so the lsp has enough information to avoid incorrect errors.

256: (add-to-list 'auto-mode-alist '("\\.cu\\'" . c++-mode))
257: (add-hook 'c++-mode-hook 'eglot-ensure)
258: 

6.6.5. Processing (Art)

Add support for Processing files (Java)

259: (setq processing-location "/usr/local/bin/processing-java")
260: (setq processing-application-dir "/Applications/Processing.app")
261: (setq processing-sketchbook-dir "~/Documents/Processing")
262: 

6.7. Wrap up

263: ;; emacs -eval "(message (emacs-init-time))" -Q
264: (message (emacs-init-time))
265: 
266: (provide 'init)
267: ;;; init.el ends here

7. Acknowledgements

Big thanks to:

  • literatemacs for demonstrating the use of org for the user init
  • org-html-themes for providing export functionality to a pretty html theme
  • Charl Botha's stackoverflow answer to have separate custom.el
  • Emacs Wiki and the emacs manual for understanding miscellaneous emacs/elisp topics.
  • Overleaf latex examples for syntax highlighting.
  • Various other forums and resources on emacs configurations.1

8. License

Copyright (C) 2023 Bidipta Sarkar

Author: Bidipta Sarkar

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

:license-gpl-blue.svg

Footnotes:

1

I know my LaTeX support was largely based on some website, but I can't find it anymore. If anyone knows where to find it, I'll edit this list to add that link.

Author: Bidipta Sarkar

Email: bidiptas13@gmail.com

Created: 2024-11-13 Wed 23:21

Validate