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:
- Installing emacs with GUI and web browsing support
- Automatic reloading and html creation for the config
- Support for conda virtual environments, including on virtual machines
- LaTeX setup with synctex
- Eglot as an LSP client for python
This file is also exported as a Github pages site here. The repo is here.
This section goes over the base installation procedure for vanilla emacs and LaTeX.
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-modern-alecive-flatwoken-icon
osascript -e 'tell application "Finder" to make alias file to posix file "/opt/homebrew/opt/emacs-plus@30/Emacs.app" at posix file "/Applications" with properties {name:"Emacs.app"}'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 listingsBefore 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 libgccjitI 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 installIf 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=basicYou 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-ofTo 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).shAfter 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.10You will be able to activate and deactive this on the terminal, and integrate this environment (along with new and existing conda environments) into emacs
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 ~/.virtualenvsOn GNU/Linux, you can link mambaforge directly instead:
ln -s ~/miniforge3/envs ~/.virtualenvsIf 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.elAfter 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.orgyou 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.
[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).
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
;;; early-init.el --- Early initialization
;;; Commentary:
;;
;; Using early-init for speed.
;;
;;; Code:
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
(add-hook 'emacs-startup-hook
(lambda ()
(setq gc-cons-threshold 16777216
gc-cons-percentage 0.1)))
(defun doom-defer-garbage-collection-h ()
"Defer gc."
(setq gc-cons-threshold most-positive-fixnum))
(defun doom-restore-garbage-collection-h ()
"Restore gc."
(run-at-time
1 nil (lambda () (setq gc-cons-threshold 16777216))))
(add-hook 'minibuffer-setup-hook #'doom-defer-garbage-collection-h)
(add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h)
;; package-initialize already in init.el
(setq package-enable-at-startup nil)
;; Inhibit resizing frame
(setq frame-inhibit-implied-resize t)
;; Disable before actual init
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
(when (featurep 'ns)
(push '(ns-transparent-titlebar . t) default-frame-alist))
(push '(width . 0.5) default-frame-alist)
(push '(height . 0.5) default-frame-alist)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; early-init.el ends hereThe code below gets constructed into the init.el, where most of the customization lies.
;; .emacs.d/init.el --- Init for Emacs
;;; Commentary:
;; Lightweight Emacs config file to make Emacs look like atom
;; while having tramp/python/c++/latex support.
;;; Code:
;; This is only here to resolve flymake complaints
(eval-when-compile
(defvar xwwp-search-prefix)
(defvar org-image-actual-width)
(defvar org-support-shift-select)
(defvar org-latex-listings)
(defvar org-src-fontify-natively)
(defvar org-file-apps)
(defvar TeX-auto-save)
(defvar TeX-parse-self)
(defvar TeX-view-program-selection)
(defvar TeX-source-correlate-start-server)
(defvar TeX-source-correlate-method)
(defvar TeX-source-correlate-mode)
(defvar reftex-plug-into-AUCTeX)
(defvar reftex-bibliography-commands)
(defvar markdown-command)
(defvar LaTeX-mode-map)
(defvar corfu-map)
(defvar corfu-popupinfo-delay)
(defvar tramp-use-ssh-controlmaster-options)
(defvar tramp-verbose)
(declare-function pdf-loader-install nil)
(declare-function TeX-revert-document-buffer nil)
(declare-function pyvenv-mode nil)
(declare-function pyvenv-workon-home nil)
(declare-function global-corfu-mode nil)
(declare-function corfu-popupinfo-mode nil)
)
Here I define that melpa should be added to the package archives, which has a larger repository of user-created packages.
;; ===================================
;; MELPA Package Support
;; ===================================
;; Enables basic packing support
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(when (not package-archive-contents) (package-refresh-contents))
(defvar my-packages
'(atom-one-dark-theme ;; melpa
corfu
jupyter ;; melpa
htmlize
magit
markdown-mode
pdf-tools
pyvenv ;; melpa
use-package
xwwp ;; melpa
processing-mode ;; melpa
))
;; Iterate on packages and install missing ones
(dolist (pkg my-packages)
(unless (package-installed-p pkg)
(package-install pkg)))
Adding basic customization options, like loading the theme and setting a custom-file so it does not conflict with this init file.
;; ===================================
;; Basic Customization
;; ===================================
(load-theme 'atom-one-dark t)
(setq inhibit-startup-message t) ;; Hide the startup message
(setq column-number-mode t) ;; Show column number in mode-line
(global-superword-mode t) ;; Superword for all buffers
(delete-selection-mode t) ;; Delete when typing over selection
(setq split-height-threshold nil) ;; Do not split by height by default
(setq split-width-threshold 0) ;; Split by width by default
(electric-pair-mode t) ;; Default electric pairs (opening parentheses create closing)
(setq use-short-answers t) ;; Use y/n instead of yes or no
(setq make-backup-files nil) ;; Do not save the backup files ~filename
(windmove-default-keybindings 'meta);; Use option key with arrows to switch which window is active
;; separate custom.el so users can set preferences separately
(defconst custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file t)
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.
;; ===================================
;; macOS Default Keys
;; ===================================
(setq mac-option-modifier 'meta
mac-command-modifier 'super
mac-right-option-modifier 'meta)
(when (string= system-type "darwin") (defvar dired-use-ls-dired nil))
(defun comment-or-uncomment-region-or-line ()
"Comments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)))
(global-set-key (kbd "s-Z") 'undo-redo)
(global-set-key (kbd "s-/") 'comment-or-uncomment-region-or-line)
(global-set-key (kbd "C-s-f") 'toggle-frame-fullscreen)
(global-set-key (kbd "M-s-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "M-s-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "M-s-<up>") 'shrink-window)
(global-set-key (kbd "M-s-<down>") 'enlarge-window)
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.
;; ===================================
;; xwwp setup
;; ===================================
(use-package xwwp)
(setq xwwp-search-prefix "https://duckduckgo.com/?q=")
Enable line numbers, word wrap, and spell checking for text modes.
;; ===================================
;; Writing Modes
;; ===================================
(defun human-text-on ()
"Turn on human text options."
(turn-on-visual-line-mode)
(display-line-numbers-mode)
)
;; Txt support
(add-hook 'text-mode-hook #'human-text-on)
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.
;; Org support
(setq org-image-actual-width 500)
(add-hook 'org-mode-hook #'human-text-on)
(setq org-support-shift-select t)
(setq org-latex-src-block-backend 'listings)
(use-package htmlize)
(setq org-src-fontify-natively t)
(setq browse-url-browser-function 'xwidget-webkit-browse-url)
(setq org-file-apps
'((auto-mode . emacs)
("\\.x?html?\\'" . (lambda (file link) (xwidget-webkit-browse-url (concat "file://" link))))
("\\.mp4\\'" . "vlc \"%s\"")))
(org-babel-do-load-languages 'org-babel-load-languages
'(
(python . t)
(shell . t)
))
Write markdown documents, common for project README files.
;; Markdown support
(use-package markdown-mode
:ensure t
:mode ("README\\.md\\'" . gfm-mode)
:init (setq markdown-command "multimarkdown"))
Write LaTeX documents with support for previewing and syncing (like double clicking on overleaf previews).
;; Latex support
(pdf-loader-install)
(add-hook 'pdf-view-mode-hook 'pdf-view-dark-minor-mode)
(add-hook 'eww-mode-hook 'pdf-tools-install)
(use-package tex
:ensure auctex)
(add-hook 'LaTeX-mode-hook
(lambda()
(turn-on-reftex)
(flyspell-mode)
(setq TeX-auto-save t)
(setq TeX-parse-self t)
(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
TeX-source-correlate-start-server t)
(setq TeX-source-correlate-method 'synctex)
(setq TeX-source-correlate-mode t)
(setq-default TeX-master nil)
(global-set-key (kbd "C-c C-g") 'pdf-sync-forward-search)
(setq reftex-plug-into-AUCTeX t)
(setq reftex-bibliography-commands '("bibliography" "nobibliography" "addbibresource"))
(define-key LaTeX-mode-map (kbd "$") 'self-insert-command)
)
)
(add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)
Work with programming modes, even in remote systems using TRAMP.
;; ===================================
;; Programming Modes
;; ===================================
(add-hook 'prog-mode-hook 'display-line-numbers-mode) ;; Show line numbers for programming languages
(use-package corfu
:custom
;; (corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
(corfu-auto t) ;; Enable auto completion
;; (corfu-separator ?\s) ;; Orderless field separator
;; (corfu-quit-at-boundary nil) ;; Never quit at completion boundary
;; (corfu-quit-no-match nil) ;; Never quit, even if there is no match
;; (corfu-preview-current nil) ;; Disable current candidate preview
;; (corfu-preselect 'prompt) ;; Preselect the prompt
;; (corfu-on-exact-match nil) ;; Configure handling of exact matches
;; (corfu-scroll-margin 5) ;; Use scroll margin
:bind
(:map corfu-map
;; Option 1: Unbind RET completely
("RET" . nil))
:init
(global-corfu-mode))
(use-package corfu-popupinfo
:init
(corfu-popupinfo-mode)
(setq corfu-popupinfo-delay '(1.0 . 0.1)))
(use-package emacs
:init
(setq completion-cycle-threshold 3))
Elisp syntax checking for emacs configuration files.
(add-hook 'emacs-lisp-mode-hook 'flymake-mode)
Python with eglot as an LSP. Note that you may need to pip install a language server after activating a virtual environment.
;; Python with eglot
;; (add-hook 'python-mode-hook 'eglot-ensure)
(setq-default eglot-workspace-configuration
'(:pylsp (:skip_token_initialization t
:plugins (:ruff (:enabled t
:formatEnabled t)
:pylsp_mypy (:enabled t)))
))
Connecting virtual environments on local and remote systems.
;; Conda and TRAMP setup
(pyvenv-mode 1)
(defun tramp-conda-setup()
"Set up conda for tramp."
(when default-directory (if (file-remote-p default-directory)
(setenv "WORKON_HOME" (concat (file-remote-p default-directory) "~/.virtualenvs"))
(setenv "WORKON_HOME" "~/.virtualenvs"))))
(advice-add #'pyvenv-workon-home :before #'tramp-conda-setup)
(setq tramp-use-ssh-controlmaster-options nil)
(setq exec-path (append exec-path '("/afs/.ir/users/b/i/bidiptas/bin")))
(setq tramp-verbose 6)
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.
(add-to-list 'auto-mode-alist '("\\.cu\\'" . c++-mode))
(add-hook 'c++-mode-hook 'eglot-ensure)
Add support for Processing files (Java)
(setq processing-location "/usr/local/bin/processing-java")
(setq processing-application-dir "/Applications/Processing.app")
(setq processing-sketchbook-dir "~/Documents/Processing")
;; emacs -eval "(message (emacs-init-time))" -Q
(message (emacs-init-time))
(provide 'init)
;;; init.el ends hereBig 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.[fn:1]
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 https://www.gnu.org/licenses/.
[fn: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.