Skip to content

swarnendubiswas/dotemacs

Repository files navigation

GNU Emacs

This repository lists my configuration for GNU Emacs, my primary editor. The install instructions and customizations should work on a GNU/Linux platform and are not extensively tested on Windows. Suggestions and pull requests are welcome.

Install GNU Emacs

The Emacs version on Ubuntu is outdated. There are a couple of PPAs [Emacs stable releases, Ubuntu Emacs Lisp] that we can use, but they do not include build flags that I need. So, I prefer to build from source [*Build GNU Emacs].

Build GNU Emacs

Add the following repository only if you are using Ubuntu 20.04.

sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
# Install gcc-10 packages with the required support for libgccjit
sudo apt install -y gcc-10 g++-10 libgccjit0 libgccjit-10-dev libjansson4 libjansson-dev

Use the following instructions to configure and build GNU Emacs from the source (Ubuntu 20.04 or 22.04).

wget https://ftp.gnu.org/gnu/emacs/emacs-29.1.tar.xz
tar -xf emacs-29.1.tar.xz

# Use GCC 10 if you are on Ubuntu 20.04
export CC=/usr/bin/gcc-11 CXX=/usr/bin/g++-11
make distclean
./autogen.sh
./configure --with-cairo --with-modules --without-compress-install --with-x-toolkit=no --with-gnutls=ifavailable --without-gconf --without-xwidgets --without-toolkit-scroll-bars --without-xaw3d --without-gsettings --with-mailutils --with-native-compilation=aot --with-json=ifavailable --with-harfbuzz --with-imagemagick --with-jpeg --with-png --with-rsvg --with-tiff --with-wide-int --with-xft --with-xml2 --with-xpm --with-gif --with-threads --with-included-regex --with-zlib --without-sound --with-dbus --without-pop --with-dbus --with-tree-sitter=ifavailable CFLAGS="-O3 -mtune=native -march=native -fomit-frame-pointer -funroll-loops -floop-parallelize-all" prefix=/usr/local

# Use NATIVE_FULL_AOT=1 to native compile ahead-of-time all the elisp files included in the Emacs
# distribution instead of after startup
make -j"$(nproc)" NATIVE_FULL_AOT=1
sudo make install

Try the following if the build fails: make bootstrap or rm lisp/loaddefs.el; make;.

You can check whether “NATIVE__COMP” is part of the system-configuration-features variable. Run the following to native compile all Elisp files under a directory (e.g., ~/.emacs.d): (native-compile-async "//home/swarnendu//.emacs.d/elpa" 'recursively)

Setup Emacs

Back up the contents of your .emacs.d directory if it is not empty, and then check out the source.

git clone https://github.com/swarnendubiswas/dotemacs.git .emacs.d

Directory structure

  • modules - Emacs modules divided across features
  • extras - third-party packages (may not be available from the package archives)
  • snippets - custom snippets
  • references - documentation and help files
  • dir-locals-examples - examples to show how to use directory-local variables
  • projectile-examples - projectile configuration files

The following are examples of a few customization options defined in init-config.el. Please check the file for more customization options.

  • sb/theme – Set the desired theme from a bunch of themes (default is modus-vivendi)
  • sb/modeline-theme – Set the desired modeline theme (default is powerline-nano)
  • sb/fill-column – Column beyond which lines should not extend (default is 100)
  • sb/minibuffer-completion - Choose either ivy or vertico for narrowing and selection (default is vertico)
  • sb/capf - Choose either company or corfu for in-buffer completion at point (default is company)
  • sb/tab-bar-handler - Choose either awesome-tab or centaur-tabs for tabbed display (default is centaur-tabs)
  • sb/project-handler - Choose either project.el or projectile for handling projects (default is projectile)
  • sb/lsp-provider - Choose between lsp-mode and eglot (default is lsp-mode)
  • sb/disable-package.el - Disable package.el and prefer the straight.el package manager.
  • sb/python-langserver - Choose between pylsp and pyright as the Python language server (default is pylsp).
  • sb/delete-trailing-whitespace-p - Control whether trailing whitespace should be deleted

LSP

We enable LSP support for the more popular languages supported with either lsp-mode or eglot. You may need to install the necessary language servers separately to complement the packages. Use M-x lsp-install-server or check setup-emacs.sh.

Installing servers for Eglot

npm install -save-dev @emacs-grammarly/grammarly-languageserver

LTEX_VER="16.0.0"
sudo apt-get install -y libslf4j-java liblog4j1.2-java
wget https://github.com/valentjn/ltex-ls/releases/download/${LTEX_VER}/ltex-ls-${LTEX_VER}-linux-x64.tar.gz
tar xf ltex-ls-${LTEX_VER}-linux-x64.tar.gz

JDT_VER="1.27.1"
wget https://github.com/eclipse/eclipse.jdt.ls/archive/refs/tags/v${JDT_VER}.tar.gz
tar xf v${JDT_VER}.tar.gz
cd eclipse.jdt.ls-${JDT_VER}
./mvnw clean verify -DskipTests=true
# Add "eclipse.jdt.ls-${JDT_VER}/org.eclipse.jdt.ls.product/target/repository/bin" to $PATH
# rm -rf eclipse.jdt.ls-${JDT_VER} 

Support for Tags

Support for LSP in GNU Emacs means you will usually not need to create tags separately. However, tags are still useful for languages that are not yet supported by lsp-mode and to understand complicated project structures that a language server may not understand. I prefer Universal Ctags over GNU Global.

GNU Global has better database search support while Universal Ctags supports more languages. GNU Global can be built with support for Universal Ctags.

Universal Ctags

Use Universal Ctags (u-ctags) with Citre.

  • -R – recursively scan for files
  • -e – use Emacs-compatible syntax
  • --list-excludes – check which patterns are excluded from processing
  • --list-languages – list supported languages
  • --languages=Python – include Python files

By default, Emacs expects a tag file by the name TAGS in the current directory. Once the tag file is built, the following commands exercise the tag indexing feature.

  • M-x visit-tags-table <RET> FILE <RET> – Select the tag file FILE to use
  • M-. [TAG] <RET> – Find the first definition of TAG. The default tag is the identifier under the cursor.
  • M-* – Pop back to where you invoked M-.
  • C-u M-. – Find the next definition for the last tag

For more commands, see the Tags topic in the Emacs info document.

ctags -eR --exclude=*.py --exclude=*.json --exclude=*.js --exclude=build* --exclude=*.sh --exclude=*.xml --exclude=*.java --exclude=*.html --exclude=*.md --exclude=*.pbtxt --exclude=*.png --exclude=*.css --exclude=*.rst --exclude=bazel-* --exclude=doc --exclude=node_modules --exclude=.meteor --exclude='packages/*/.build/'

ctags -e -R [email protected] --languages=EmacsLisp .
ctags -eR -quiet=yes [email protected] .
ctags -eR --languages=Python

find . -name "*.tex" | ctags -e -quiet -L -
find src -name "*.py" | ctags -e -L -

find -L . -type f -iname "*.cpp" -o -iname "*.c" -o -iname "*.cc" -o -iname "*.h" -o -iname "*.hpp" -o -iname "*.cu" | ctags -e -L -
$ cat .ctagsignore
dir1
dir2
dir3

GNU Global

Use GNU Global with counsel-gtags: gtags -cv --gtagslabel=new-ctags

find -L . -type f -iname "*.cpp" -o -iname "*.c" -o -iname "*.cc" -o -iname "*.h" -o -iname "*.hpp" -o -iname "*.py" ! -iname "*.cu" -o -iname "*.proto" | gtags -cv --gtagslabel=new-ctags -f -

find ./src -type f -iname "*.py" ! -iname "__init__.py" | gtags -cv --gtagslabel=new-ctags -f -

find . -type f -iname "*.tex" | gtags -vc --gtagslabel=new-ctags -f -

Configuring Emacs Daemon

Enable server support either through init.el or as a systemd service. I prefer the systemd approach. Create a file $HOME/.config/systemd/user/emacs.service with the following content.

[Unit]
Description=GNU Emacs Daemon

[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon
ExecStop=/usr/bin/emacsclient --eval "(progn (setq kill-emacs-hook 'nil) (kill-emacs))"
Restart=always

[Install]
WantedBy=default.target
  • Enable the unit to start at login: systemctl --user enable emacs.service
  • Disable the unit to start at login: systemctl --user disable emacs.service
  • Start the service for the current session: systemctl --user start emacs.service
  • Stop the service for the current session: systemctl --user stop emacs.service
  • Restart the service for the current session: systemctl --user restart emacs.service

Desktop Entry

Create emacs.desktop and emacsclient.desktop files in $HOME/.local/share/applications with the following content.

[Desktop Entry]
Name=GNU Emacs
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=/usr/local/bin/emacs
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;Utility;
StartupWMClass=Emacs
Keywords=Text;Editor;
[Desktop Entry]
Name=GNU Emacsclient
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
Exec=emacsclient -c -a "" -n -F "'(fullscreen . maximized)" %f
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;Utility;
StartupWMClass=Emacs
Keywords=Text;Editor;

Emacs in a Terminal

I use LSP over Tramp intensively, and LSP+Tramp is sluggish and fails often. It seems difficult to properly set up language servers with Tramp support. Therefore, I prefer to use Emacs in a terminal that has a much better performance. It is important to set up support for 24-bit colors and proper keybindings in the terminal for a good experience. I use Alacritty which is easy to customize.

Using Terminal Emacs over Tramp for editing remote files obviates the need for a remote langsever.

Use the steps mentioned in the link Spacemacs Terminal to enable support for 24bit colors in the terminal.

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export TERM=xterm-24bit

Using export TERM=xterm-24bit may lead to failures when accessing remote systems. In such cases, we can fall back to TERM=xterm-256color ssh -X <remote-path>.

Emacsclient

Start the Emacs daemon with emacs --daemon. To run Emacsclient with a GUI, use emacsclient -c -a "", where -a "" starts Emacs daemon and reattaches. To run Emacsclient in a terminal, use emacsclient -nw -q or emacsclient -t -q -a "", where -t starts the client in the terminal.

Add the following to your .bashrc to use Emacsclient as your editor.

export ALTERNATE_EDITOR=""
export EDITOR="emacsclient -t"                  # $EDITOR opens in terminal
export VISUAL="emacsclient -c -a emacs"         # $VISUAL opens in GUI mode

Debugging Emacs

  • kill -s USR2 [pid]
  • killall -s USR2 emacs
  • pkill -USR2 emacs

Profile startup time

Estimate the best possible startup time with emacs -q --eval​‘(message “%s” (emacs-init-time))’=. There are a few choices to evaluate the performance of the configuration.

  • Set use-package-compute-statistics and then invoke use-package-report
  • Use the package benchmark-init
  • Use the script profile-dotemacs.el as follows: emacs -Q -l $HOME/.emacs.d/extras/profile-dotemacs.el -f profile-dotemacs
  • Advanced Techniques for Reducing Emacs Startup Time

Known Bugs

  • Flickering with corfu-terminal-mode
  • Disable pairing = before at the beginning of a word in smartparens
  • Enable flycheck-mode for Elisp configuration files
  • Load custom snippets with yasnippet
  • Disable prettifying of symbols on auto-completion in LaTeX-mode