Topi Kettunen


Finally Got My Emacs Setup Just How I Like It

Posted on 24th of February 2023 | 745 words

So in recent days, I have stumbled upon some REALLY NICE (at least in my own standards) Emacs tweaks, which I wanted to share with you.

First, something very trivial, I knew that Emacs had some sort of jump to previous location etc., type of feature available, but I never got into using it. Turns out there’s is a built-in keybinding for that or a couple. First, one was C-x C-x, which jumps to the last position and selects the text from your current position. So, e.g. you jump to the beginning of a file and press that combination, it selects the text from the beginning to the last position. Which was cool for me, but I rarely need something like that.

That was enough for me for some time, but I wanted to tweak it slightly. I didn’t care about selection and wanted to centre the screen after the jump. Thankfully, this is relatively trivial to implement in Emacs with a single function:

(defun jump-to-mark-and-center (arg)
  (interactive "*p")
  (goto-char (mark))
  (recenter))

(global-set-key (kbd "C-x C-x") 'jump-to-mark-and-center)

So I bound the old keybinding to that new function, which does exactly what I want. Lovely!

To the second lovely new configuration! I had longed for a feature in Emacs, where I could find a file based on the format of FILENAME:LINENUMBER. So, if I would have a file, let’s say file.cc and I would immediately jump to the line number 14 in that file, I would want to open that file with file.cc:14 like most of the Unix tools print these file locations, but I just couldn’t do it in Emacs.

Thankfully, after some searching throughout the interwebs, I found some nice defadvice that fixes this for me:

(defadvice find-file (around find-file-line-number
                             (filename &optional wildcards)
                             activate)
  (save-match-data
    (let* ((matched (string-match "^\\(.*\\):\\([0-9]+\\):?$" filename))
           (line-number (and matched
                             (match-string 2 filename)
                             (string-to-number (match-string 2 filename))))
           (filename (if matched (match-string 1 filename) filename)))
      ad-do-it
      (when line-number
        ;; goto-line is for interactive use
        (goto-char (point-min))
        (forward-line (1- line-number))))))

And BAM! It works just like that!

And the last lovely new feature! I use mainly vterm inside my Emacs for my terminal needs. I always wanted to use it so that when I’m inside a certain directory in the terminal, I could just open some file in that directory, but unfortunately, by default, vterm only knows the directory where it was opened at.

Thankfully, after reading some documentation about vterm, it turns out you’re able to send certain character codes to emacs from your vterm session. So you’re able to make it so that when you open vterm in directory x and proceed to change the directory inside the vterm with many different cd commands etc. to something like x/many/different/subdirs, when I run something like C-x C-f in that vterm buffer, the minibuffer inside Emacs, would know that I want to file directory in the directory where vterm currently is, instead of the directory where it was initially opened.

This can be done by doing some shell tweaking. I use zsh myself, if you use something else, refer to vterm README .

# Enable the shell to send information to vterm via properly escaped
# sequences.
vterm_printf() {
  printf "\e]%s\e\\" "$1"
}

vterm_prompt_end() {
  vterm_printf "51;A$(whoami)@$(hostname):$(pwd)"
}

 Let vterm know what dir I'm in
setopt PROMPT_SUBST
PROMPT=$PROMPT'%{$(vterm_prompt_end)%}'

If you happen to use screen or tmux, you might need to do some other tweaks in there, but these are mentioned in the vterm README.

In any case, when you define those to your .zshrc, vterm sends the information of the current directory straight to Emacs, so it knows where you’re currently at. Which is great!

To make that even better, I often noticed that I wanted to open files straight from the command line instead of running some Emacs command to open the files. Fortunately, vterm covers this also:

vterm_cmd() {
  local vterm_elisp
  vterm_elisp=""
  while [ $# -gt 0 ]; do
    vterm_elisp="$vterm_elisp""$(printf '"%s" ' "$(printf "%s" "$1" | sed -e 's|\\|\\\\|g' -e 's|"|\\"|g')")"
    shift
  done
  vterm_printf "51;E$vterm_elisp"
}

find_file() {
  vterm_cmd find-file "$(realpath "${@:-.}")"
}

alias e="find_file"

With these functions inside your .zshrc, I can run find_file inside vterm, and it opens the file in your current Emacs session. I just use short alias to run e somefile inside the terminal; it opens a new buffer for the file.

I have used Emacs for a long time, but these recent additions made it so much nicer. Hopefully, these are helpful for you too.