Opening Files with Line Numbers in Emacs

1 minute read

Long ago I shared my EmacsClient setup. The short of it was that I always have Emacs running and set $EDITOR in such a way that things like git commit open a new frame in my existing session. I also had a simple Bash alias that let me open any file in a frame as well:

alias ec="$EMACSCLIENT -c -n"

Where $EMACSCLIENT is the path to emacsclient on whatever system I’m on, -c is to create a new frame for the file, and -n to tell emacsclient to exit immediately instead of waiting for me to finish editing.

This has served me well for years. However, there’s one bit of laziness I’ve been wanting to implement. Handling filenames (or file paths) that include line numbers, i.e. filename.rb:123. It’s common for error messages to be spit out in the form of:

./lib/something/somefile.rb:126:in `some_method'

Likewise, test frameworks like rspec will report failing tests in the form of:

./spec/something/somefile_spec.rb:152

If I want to look at that error, I need to select the filename without the line number, paste it to open the file and then go to line 126. Being lazy, I wanted a way to do that in one step. Fortunately, EmacsClient supports opening a file at a given line by adding “+linenumber” to the command line:

emacsclient -c -n +126 lib/something/somefile.rb

I can take advantage of this by replacing my ec alias with a function and adding a bit of smarts.

function ec () {
       if [[ $1 =~ (.*):([0-9]+)$ ]]; then
          $EMACSCLIENT -c -n "+${BASH_REMATCH[2]}" "${BASH_REMATCH[1]}"
       else
           $EMACSCLIENT -c -n "$@"
       fi
   }

The if statement uses a Bash Regular Expression to test if the file path ends in a colon followed by numbers. If so, it captures the file path in $BASH_REMATCH[1] and the line number in $BASH_REMATCH[2] ($BASH_REMATCH[0] is the everything that matched without regard to the parentheses). It then adds the + option to the command line and follows that with the value of $BASH_REMATCH[2] and uses $BASH_REMATCH[1] for the file path.

If there isn’t a match, it simply uses the argument for the filename, as it always has.

Laziness and Bash functions for the win!

Comments