The Tao of Emacs

I know, the title of this essay is a cliché, but the Chinese word, 道, we transcribe as tao means a way or path, as in a way of life. As such, Emacs has a definite approach to how we work.1

No, no, no, this is not a flame-war. I’m not saying the Emacs Way is objectively better, but if you decide to use Emacs, you may find your work-style improved if you incorporate the Emacs Way.

In other words, don’t fight it, embrace it.

Emacs vs. Shell-Vi

Allow me to describe the Emacs Way by contrasting it against a different approach, the Shell-Vi Way. The following diagram illustrates how one would develop and compile a program:

tao-of-emacs-vi.png

Note: This diagram loosely shows flow not communication or control. Don’t read too much into it.

The shell is the central focus. One begins in the shell, starts a vi session to edit a file, exits to return to the shell in order to kick off gcc, and uses the shell to execute the compiled program or debugger. In this way, the editor must be quick to start, since one is constantly re-starting it.

tao-of-emacs-emacs.png

The Emacs Way replaces shell’s central position. Emacs runs commands typically done by the shell, including the compiler/interpreter, the application, and its test suite.

When people joke about Emacs being an operating system, they are not too far from the truth, for to me, an operating system is just a boot loader to get me to Emacs where I can then start doing my work.

An Example of The Way

In case you are not familiar with this approach, allow me to walk through a typical example.

Changing a File’s Permissions

Let’s say, you wish to create a new script. In a shell-oriented system, you would begin with a command to start up an editor on that new file. However, since Emacs is already running, we just find-file the new script.2

Yes, not much different, but wait…

In order to test the script, we need to change the execute bit. In the Shell Way, one would exit the editor, an run the chmod command from the shell. In the Emacs Way, you either:

  1. Type M-! and then the equivalent chmod command, or
  2. Type M-x and then chmod, hit Return to select your file (as the current buffer is the default value), and then the permission’s value.

With the Emacs Way, one doesn’t exit the editor simply to change the value.3

Using a REPL

Another subtle, but significant difference is how Emacs handles a REPL. For years, when talking to my C colleagues (and then my Java colleagues), a REPL seemed odd. However, dynamic languages for the JVM (like Scala, Groovy and Clojure) have made this less odd.

A REPL allows you to run an interpreter and give it commands and expressions to evaluate. For instance, with Ruby, you would run irb, and then type some expressions, like this:

$ irb
>> 5 + 6
=> 11
>> 1 + rand(6)
=> 3
>> 1 + rand(6)
=> 6
>> def die
>>    1 + rand(6)
>> end
=> nil
>> die
=> 3
>> die
=> 1
>> (1..3).collect { die }
=> [4, 3, 6]
>> (1..3).collect { die }.reduce(:+)
=> 10
>> def dice(num=2)
>>    (1..num).collect { die }.reduce(:+)
>> end
=> nil
>> dice
=> 6
>> dice
=> 11
>> dice(5)
=> 19

The output of every REPL for each language is different (and in this case, irb doesn’t color my input… that is just the side-effect of my export mechanism), but if you’ve never tried a REPL, this should give you a view.

This is a great way to import and investigate a new library or try out some experimental code. While the instant feedback is great, the actual user interface leaves a bit to be desired. Somes REPLs are nicer than others, for instance, bpython. is quite slick for a terminal-only application.

Emacs can give you the best of both a REPL and an editor.

Let’s walk through the above Ruby session, keeping in mind that one can often to have a similar approach in many other languages. Begin with: M-x and inf-ruby (I type ir and SMEX will auto-complete it).4

We can now type 5 + 6 and all the other expressions we did in the above REPL. Yeah, no big whoop here. Open another buffer (C-x b if you’re following along at home) called blah.rb, and type:

def die
  1 + rand(6)
end

With the cursor anywhere in there, issue M-x ruby-send-block (or C-c C-b) to send this function definition over to the REPL. Notice a bit of gibberish, but it will end with:

=> nil

Which is the results of evaluating a function definition in Ruby.

Pop into that original ruby buffer, and type die and return and you’ll be treated with random values from rolling our virtual die.

Better yet, back in your Ruby buffer, type die on a line by itself and hit C-x C-e (or M-x ruby-send-last-sexp), and the die function call is sent to the REPL and the results of the function is displayed.

The point of this exercise is that you can type some code and either select part of it or send a full block to the REPL to be evaluated.

Think about writing unit tests, and evaluating the expression to make sure it matches the assert as you go along. For this, I often use Magnar Sveen’s Expand Region project to easily select some section of code, evaluate it (that is C-c C-r), and expand the region even more to validate a larger section.

Let’s demonstrate with a slightly larger Ruby example. Add this to your blah.rb file:

def dice(num=2)
    (1..num).collect { die }.reduce(:+)
end

With your cursor anywhere inside the num=2, repeatedly hit the C-= key binding (whatever you have bound to the expand-region function) until everything inside the parenthesis is selected. Then C-c C-r to send that expression to the Ruby REPL. This assigns a global variable, num to 2, but that is fine for now.

Next, with your cursor on the opening paren in the expression: (1..num) hit C-= to select that expression, and again, C-c C-r to evaluate that expression. Yeah, it returns 1..2 which isn’t too exciting.

Hit C-= to add the .collect part to our highlighted region, and evaluate it with C-c C-r, and you should see [1, 2] in the REPL. Slightly more interesting.

tao-of-emacs-ruby1.png

If you hit C-= again, you’ll select the entire block, which is too much, as we just want to trailing block. Since our cursor is at the beginning, we hit C-x C-x to put the cursor at the end of the region, and C-8 C-f to move forward 8 characters to add the block, { die } to the region. Now evaluate that. We should get the results of rolling two die. Evaluate it again to get a different roll.

tao-of-emacs-ruby2.png

Hit C-e to add the reduce statement to the region, and evaluate that to get a number from 2 to 12. I’ve now validated to myself that my dice rolling function is good enough, but I wouldn’t be happy until I popped over to my test suite and started building a unit test in a similar way.

Remote Server Files

What if the files to edit and the commands to run are located on a remote server? No problem. No, you don’t need an instance of Emacs running on the remote server…just use your local Emacs application with Tramp.

Call the normal find-file function (C-x C-f), but pre-pend the host’s name, as in:

/ssh:10.52.224.67:blah.txt

Set ssh as the default Tramp protocol in your Emacs configuration file:

(setq tramp-default-method "ssh")

Then you can shorten the file specification to:

/10.52.224.67:blah.txt

Copy over your SSH public key to the remote system, and Tramp won’t have to ask you for the password, since Tramp just uses ssh and other standard tools under the hood.

You can specify a different user account:

/bob@10.52.224.67:blah.txt

Once you’ve specified the host, tab completion works, and the list of files show up as if they were local.

At my day job, remote systems are located in a data center behind multiple firewalls. I minimize the accessibility issues by placing lines like the following in my ~/.ssh/config file:

Host  10.2.3.4       # The Bastion
      Port 66
      DynamicForward 43536

Host  10.98.19.*
      ProxyCommand nc -x localhost:43536 %h %p

This tells me that I can get to machines on the 10.98.19.* subnet through a local connection on port 43536, when that port is first established by connecting to 10.2.3.4.

My day job is building private clouds, and often this form of SSH tunneling just isn’t possible. However, you can use Tramp to jump from a bastion to an inaccessible host by specifying a series of jumps where each step is separated with the | character.

For instance:

/10.98.19.229|10.0.1.85:bling.txt

This just uses the regular SSH calls under the hood to first log into 1-.98.19.229 and then start another ssh connection from that system to 10.0.1.85 to get the file, bling.txt. This is so cool since I also can log into a system, and then tell it to use sudo to access an root-owned file:

/ssh:10.98.19.229|ssh:10.0.0.16|sudo:10.0.0.16:/etc/httpd/httpd.conf

In the above example, one needs to add the ssh protocols when you use the pipe symbol. Also, you need to specify the hostname directly (and don’t be tempted to use localhost), as Phil mentioned to me.5

After opening a file, commands started from Emacs (that I mentioned at the beginning of this essay) automatically work on the remote machine through the same tunnels. Once I load a file this way, I drop a bookmark (either to a register with C-x r SPC or as a full name using C-x r m), and later I can jump directly to that file without having to enter the host name.

Summary

Once again, I am not advocating that The Emacs Way is objectively better. I am, however, cautioning new Emacs uses not to simply swap the word, vi for emacs if you are using the Shell Way. Start Emacs once and leave it running as long as your session… in my case, that can be months until the next operating system upgrade requires an Emacs restart.

Footnotes:

1

Emacs didn’t invent the Emacs Way. RMS originally borrowed the concept of shortcut keys (specifically key bindings that manipulate a document without altering the state), but this feature of Emacs clearly influenced many an IDE since.

While IDEs attempt to keep you inside the editor in a way similar to Emacs, I don’t consider extending a system with plugins (even with an open plugin architecture like that adopted by Eclipse) to compare to extending a development environment with functions (this idea was first started with Smalltalk and appropriated into Emacs soon after).

Of course, discussing historical influences can be tricky, so if you’d like to correct me, feel free to drop me a line.

2

I think I used vi four or five years before I learned it could load a second file without exiting. ;-)

3

Yes, vim and other good editors can run a shell command without requiring a re-start of the editor, however, this isn’t the normal workflow.

4

While the default Emacs comes with the ability to edit Ruby files, the REPL integration is done with the inf-ruby package. To install it, type M-x package-install (tab completion or SMEX saves you from typing it all), and type inf-ruby at the prompt.

Please, see my complete Ruby Initialization code for Emacs for details.

5

Using localhost in an sudo in an ad-hoc multi-hop tramp path is incorrect. While it seems to work fine, Phil informed me that it introduces a problem. See this StackOverflow discussion for details.

Phil wrote:

The trap with using sudo:localhost is very similar to the trap with using sudo:: with remote hosts– in this case the HOST for the dynamic proxy entry becomes “localhost”, and so if you subsequently attempted to open /sudo:localhost:/path on your local server, you are instead proxied to the remote host from the earlier multi-hop path!

Or perhaps more likely than that (because on the local host you would simply use /sudo::/path after all), consider what happens when you use the sudo:localhost approach for multiple remote hosts – you will shadow the proxy for root@localhost each time you do this, after which all but one of those remote+sudo paths would go to the wrong server!

When multi-hopping, you should always use the explicit remote hostname for the su and sudo methods.

p.s. I got here via http://irreal.org/blog/?p=3750 where I had commented on this issue on account of your article having no comments facility, before subsequently noticing you did in fact have this contact link.