Which Text Editor/IDE is best?

You are probably thinking, based on the title and headings so far, that this post is going to be all about how Emacs is the best editor since it is, right? Actually, I don't believe that there is one editor that beats all others. Some IDEs are necessary for developing on specific platforms (I'm looking at you Xcode). Every person should use the editor or IDE that makes them feel the most comfortable and gets the job done. That said, this post is going to be about how I came to use Emacs and what features keep me using Emacs instead of something else in my daily work. While I use it mostly for writing code, many people see it as an organizer, internet browser, email client, windows manager or an operating system. I'm writing this mostly for myself, but maybe one day someone will read this and want to try out Emacs for themselves.

Discovering Emacs

If you don't care about how I came to use Emacs, you can jump straight to the section on my favorite features.

What editor do you use?

My Emacs journey begins on my first day as a software engineer. I was a junior in college and was starting an internship doing web development. Because I had no web experience before taking the internship, I had to rely on the motto "fake it till you make it" as I was sitting with my mentor getting my environment setup. He asked me to install my favorite editor. To most, this would be an easy question. I honestly had no idea. I didn't even know what editors were popular, and there was no way I was going to admit that to my mentor on the first day and show how inexperienced I was. Not knowing how to respond, I asked him what editor he used, and he told me he used Atom. I replied, "ah nice, me too." and proceeded to download it.

From Atom to VS Code

The next several months were full of a lot of learning. As I got more involved in the software engineering community, I heard about VS Code because it was growing in popularity at the time. I decided to try it out. Compared to Atom, I remember thinking it was amazing. Although, to be honest, the only reason I can remember liking it more was that it was faster and used less memory. I introduced VS Code to the other ten developers at the small start-up where I worked and almost all of them switched to the editor. VS Code worked great for a long time. During the next year, one of my co-workers introduced me to the Vim extension. After a small learning curve, I ended up using it every day. I did go through a rough phase that I'm not proud of where I remapped my "h" and "i" keys so that I could use arrow keys like a "normal" person (don't worry, I have since repented). I soon hit a point where coding without Vim was unnatural and impossible for my brain. I was adding "j"s and "k"s in random places if I ever coded in an editor that didn't have vim key bindings installed.

A Call from the Emacs Gods

Then the day came that changed my life (really, it did). One morning the Vim extension in VS Code stopped working. The editor would crash with the extension enabled. I tried to reinstall but I couldn't get it working. It was as if the Emacs gods were calling to me. The engineer that sat next to me at work, who happened to be a long time Emacs user, saw my distress and told me I should try using Spacemacs. He then sat down with me and for the next two hours (yes, it took that long) got me all set up in Spacemacs to the point where I could go about my work productively. Without his help, I probably never would have joined the Emacs community. That day marked the start of a long and happy journey into the Emacs world.

Goodbye Spacemacs, Hello Doom

I used Spacemacs for probably around six months. Everything worked and I learned a lot about Emacs. Eventually, I became dissatisfied with the performance of Spacemacs. I had learned a lot of Elisp by moving my config into layers and writing a lot of custom functions. I thought I was ready to branch out into the world of vanilla Emacs. I never did find out if I was ready for that. I read a Reddit post suggesting people should try out Doom Emacs if they were frustrated with Spacemacs performance and I decided to give it a shot. I'm glad I did because I have loved Doom and feel like things work out of the box, and they work well. Doom doesn't try to hide tons of implementation details from you. Adding extra configuration was easy to do as well. That brings us to the present, two years after I used Emacs for the first time. Emacs has become both a hobby and a tool that I use every day.

Why I Stuck with Emacs

There are several things about Emacs that make it my editor of choice. Some are simple, like being able to have Emacs cover multiple monitors (this doesn't work very well in VS Code). Others are full-featured Emacs packages.

Magit

Hands down, one of the best packages for Emacs is Magit. Magit is a git client inside of Emacs. I could probably write a whole blog post just on Magit, but the summary would be I have never found an easier way to interact with Git. When I started using Emacs, I had tried several different Git clients such as GitKraken and Sourcetree. Neither of them did it for me so I mostly just stuck to the terminal. I thought it would be the same after I started Emacs; however, once I used Magit, I found I could do things so much easier and faster. I stopped using the command line for most tasks and used Magit primarily for my Git interaction.

Magit makes it easy to view all the changes made on a specific file, stage and discard individual hunks, stash, un-stash, resolve merge conflicts, tag, and cherry-pick, etc. all with keyboard shortcuts. Oh, and the interactive rebase in Magit is magic.

Vim Emulation

One of the biggest draws to Emacs is that I never have to use the mouse. I'm not even sure it works with Emacs. A big part of that is due to the "evil" packages that provide the best Vim emulation I have seen in an editor outside of Vim. (I should note that Emacs has its own keybindings that facilitate this as well, so you don't have to use Vim bindings.) In fact, in VS Code, there are a lot of things that don't work as they should. One thing that I do frequently is highlight a section of code and then press the "=" key to auto-indent. I didn't realize how useful and dependent I was on that feature until I tried to do it in VS Code and couldn't.

Config as Code

It's important to me to be able to backup my configuration for my editor so that I can use it on any machine I want. Some editors are better at this than others. Because Emacs is all configured in Elisp files, I keep all my files in version control and, when I'm setting up a new machine, I just have to clone that repo, and my editor is all setup.

Yasnippet

Snippets aren't something unique to Emacs, but they are something that I use pretty frequently. Yasnippet allows you to expand a keyword into a block of code. I use them most often for generating new files (like classes or front-end components) and methods that I frequently use. It's a way for me to reduce the amount of repetitive typing that I do and a way to remember things that I don't do that often. One of the best parts is the snippets also show up in my auto-completion thanks to an Emacs package called Company.

Here are a couple of examples. In Ruby, an each block looks like

foo.each do |bar| 
  # Do something with bar 
end

With Yasnippet, I can type "each" and press "tab" to get Yasnppets to insert the do block. It will move my cursor to enter a variable name, then the body of the loop. It's easy for me to create my own snippet files and bind them to the keys I want. A simple snippet file will look something like the following.

# -*- mode: snippet -*-
# name: each do |var|
# key: each
# --
each do |${1:var}|
  $0
end

Yasnippet also has macros, like Yas-choose-value, built-in to make your snippets even more powerful. I often write Rails migrations (sorry for all the Ruby examples) and I forget all the options I can use when creating a new column. I use the following snippet to remind myself.

# -*- mode: snippet -*-
# name: add_column(table_name, column_name, type, default:, null:, after:)
# key: add_column                                                                                                                                                                                                                                                              
# --
add_column :${1:table_name}, :${2:column_name}, :${3:type$$(yas-auto-next(yas-choose-value'("binary" "boolean" "date" "datetime" "decimal" "float" "integer" "primary_key" "string" "text" "mediumtext" "time" "timestamp")))}${4:, default: ${5:default}}${6:, null: ${7:false}}${8:, after: :${9:column_name}}${10: if !column_exists(:$1, :$2)}$0

Here is a little demo of what Yasnippets can do.

Gif Showing Yasnippet In Action

Elisp Functions and Examples

I'm pretty sure that Emacs can do anything. If somehow there isn't already a package made to do what you want, you can write your own using Elisp.

Dart Functions

During my senior year in college, I built a couple of apps in Flutter for different classes. Emacs had Dart support for auto-completion thanks to LSP servers. While working with a friend on a project, I saw that JetBrains had a feature where you could right-click on a widget and click "wrap in another widget". That's something that you do an awful lot when writing Flutter. I added this functionality to Emacs by recording a macro, and then calling (name-last-keyboard-macro) and (insert-kbd-macro) to save the macro as a function in my Emacs config. Then I added it under my Dart keybindings. It took a whole 30 seconds to add that to Emacs.

Magit and Merge Requests

The one thing Magit doesn't do for me is to create pull requests without an additional package called magit/forge. However, that package is a little heavy for what I want to do. I don't need to see and review pull requests in Emacs, I just want to automatically open a window in my browser for the current branch that I'm on. That was easy to implement by myself with a few Elisp functions shown below.

(defun editor/current-git-branch()
  "Returns the name of the current git branch"
  (interactive)
  (call-interactively 'magit-status)
  (magit-get-current-branch))

(defun editor/create-github-pull-request()
  "Vist the current branch's PR on Github"
  (interactive)
  (browse-url
   (format "https://github.com/%s/compare/%s?expand=1"
           (editor/current-git-repository) (editor/current-git-branch))))

(defun editor/create-pull-request()
  "Detect Version Control System and Launch Pull Request in Browser"
  (interactive)
  (call-interactively 'magit-status)
  (let ((remote (magit-get "remote" (magit-get-current-remote) "url")))
    (cond ((string-match "gitlab" remote) (editor/create-gitlab-pull-request))
          ((string-match "github" remote) (editor/create-github-pull-request))
          ((string-match "bitbucket" remote) (editor/create-bitbucket-pull-request)))))

Ruby Gem Source Code

As you might have guessed from previous comments in this blog, my current job involves a lot of Ruby on Rails. In JetBrains (they do get a lot of things right), you can jump straight to definitions of source code in gems in you need to know how they are working on the inside. I do this pretty often, but I used to have to pull up the gem in GitHub to look through the code. I wrote a function that pulls up all the gems in the project in an auto-complete window, lets you choose which a gem, and drops you into the source code. I think I use this more than I even would a "jump to definition" option as it lets me jump straight to the README.md of a gem to look at its documentation straight from my editor.

(defun ruby/project-gems ()
  "Returns a list of gem names in current project"
  (split-string (shell-command-to-string "bundle list --paths") "\n"))

(defun ruby/gem-find-file ()
  "Opens up the find-file window in the gem directory"
  (interactive)
  (ivy-read "Project Gem: " (ruby/project-gems)
            :action (lambda (f)
                      (with-ivy-window
                        (counsel-find-file f)))
            :require-match t
            :caller 'ruby/gem-find-file))

(defun ruby/gem-find-folder ()
  "Opens up dired in the selected gem's directory"
  (interactive)
  (ivy-read "Project Gem: " (ruby/project-gems)
            :action (lambda (f)
                      (with-ivy-window
                        (find-dired f "-regex '.*' -maxdepth 1")))
            :require-match t
            :caller 'ruby/gem-find-folder))

Here is a preview of those Ruby functions in action.

Gif Showing Ruby Find File

Ivy and Friends

The thing that maybe annoys me the most about VS Code is how small the search bar is on the side. I love how in Emacs, when I search for things, I see the full line of text. It also is easy to search and replace text in multiple files at once. Several popular packages give you these features, but my personal favorite is called Ivy. See the recording below for an example of some of the things that Ivy makes possible.

Gif Showing Yasnippet

Thanks for Reading

And there you have it. If you are still reading at this point, thank you! I think it would be impossible to explain the power of Emacs in a single post, but I wanted to write down some of my reasons why I continue to use Emacs even though it isn't the most popular or modern editor. Hopefully, if you were considering Emacs but haven't tried it yet, you now want to go check it out. It has a pretty big learning curve when you first start, but it makes up for it over and over once you get comfortable and make Emacs work for you.