Your browser (Internet Explorer 6) is out of date. It has known security flaws and may not display all features of this and other websites. Learn how to update your browser.
X
The ideal text editor

The Ideal Text Editor

I am a Vim user and have been for more than 15 years now - with short stints to other editors from time to time. I also wrote a popular plugin for Vim - so I understand it's internals better than most. Still, I feel that Vim is showing it's age - and it is hardly the ideal editor.

I also used TextMate for a while. I tried Sublime for a week or so and am trying Atom right now - I am writing this post in it. There is also a new kid on the block (sorta) with Brackets which seems to be very focused on web design. All of them are impressive editors, but all fall short in some ways for me. Here are my musings about the ideal text editor.

Here are the most important properties of an ideal text editor for me, in sorted order.

Free

This includes free as in free-beer and free as in free-speech. I used TextMate for about a year and got plenty annoyed that I was unable to fix bugs since it was closed sourced. The developer loosing interest in the project and basically walking away also kinda sealed it's death. Imagine spending time learning a powertool, just to see it abandoned and left to bit rotting - and there is nothing you can do. TextMate seems to be back after some years now - but I would never set on a closed source editor again.

Being monetary free is important to get people involved. An editor that is just 100% free is less risk to try out than one that you might need to shell out $$$ to use.

Responsive

A text editor must react as fast as I can think. It is not acceptable to wait for simple tasks like switching a tab or running a command. I must never be interrupted while the editor needs to finish some computation - like auto completion results or a make command.

Modal

Vim's best feature is its UI. Modal editing opens up so much more abilities to control functionality. There are alternatives that are really good too - for example a fuzzy selector over all available commands. But even when this feature is available: with normal mode you have the full keyboard available for shortcuts.

It also clearly defines cutting points for what undo/redo/repeat does. And Vim's editing language (e.g. daw - delete a word) is intuitive, precise and quick.

I will never ever consider an editor that does not have strong modal support build in. It is fine if it can be turned off of course - as long as it is not implemented as an afterthought.

Easy to extend

This is a biggy. It should not matter what my main language is - as long as it is turing complete I should be able to add new features to my editor. These features should be arbitrarily powerful and seemlessly integrated, i.e. they should be indistinguishible from build in commands. Atom and Sublime are good examples of editors that get this second part pretty right.

What they both get wrong is the choice of language: Atom is JavaScript and Sublime is Python. If you do not speak the language of your editor out, you are demoted to User status.

Vim does a little better: It has support for Python, Ruby, Lua, Lisp ... you name it. Unfortunately all of these integration are second class: You cannot write a full plugin in either of these languages - you need a fair amount of VimScript to glue it together. And after 7 years of UltiSnips I can confirm that VimScript is no fun to write.

So a good editor should support as many languages as possible and all of them should be first class citizens.

Cross platform

I never work on Windows, but I work on Mac OS and on Linux a ton. I need an editor that runs on both. I think cross platform is also really important to atract people - so Windows support is important too.

Console/Remote editing

I do not enjoy working in console Vim. I like my Editor to support me with a rich GUI and I enjoy that my editor saves whenever I switch to the shell.

Howevr, occasionally I need to remote edit files on a server - I use console Vim there. That is a really important feature for me. But even better would be if I could remote edit the files: having the nice gui on my laptop talk (securely) to the editor core running on the server would be great.

A console GUI is still important, since many people still like to work in the console using tmux to never switch the program they are using. That is cool, and I think an editor should support it.

Beautiful

There, I said it. I stare at my text editor 8 hours per day - it should better look nice.

Collaborative

I love working on documents with others in Google Docs. Every collaborater can edit the document live and sees the cursor and the edit actions of others at the same time. Imagine how cool pairing would be in an editor that supports that nicely and natively.

I know that there are plugins/hacks for existing editors - but it needs to be there out of the box and easy to setup for people to actually pick it up.

Conclusion

These are the things I ask from an editor. I might have forgotten some stuff, but these are the important things. I will continue this series of blog posts with looks at the editors that are currently out and how they fare. Also I want to present a design that I think works to implement an editor close to my heart.

Design for the ideal text editor.

Design for the ideal text editor

Update: There is now a RFC for Swiboe defining this design in more detail. Comments welcome.

Last time I discussed my thoughts on the ideal text editor. I also mentioned that nothing quite fits the bill for me right now. Today, I want to discuss a possible design for an editor that I think allows for all the things that I look for and that is future proof.

I put a lot of thought into this lately - my frustration with Vim's shortcoming's in daily life and my experiences in writing one of the more successfull Vim plugins out there have given me some insights which I tried to distill in this design. I am currently exploring these ideas in code in Swiboe - my attempt at implementing the editor of by dreams. Contributions are welcome.

The best editor is no editor at all

A simple idea got me started: everything is a plugin and a plugin has infinite power. In Swiboe, these are all plugins: the buffer management (editing, creating, loading), the fuzzy searcher, the git integration, the GUI program, the cursor handling in the gui, the plugin loading - every functionality. Swiboe is really just an operator on a switchboard:

Switchboard operator. Image curtsey of Wikipedia.

That is where its name is coming from: Switch Board Editor.

But how does that work? Let's design two basic plugins that know how to open compressed files into buffers, one that can read Gzip, one that can read Bzip.

Swiboe listens on a socket and waits for plugins to connect. Once a plugin connects, it registers remote procedure calls (RPCs) which have a name and a priority.

# In gzip.py
plugin = connect_to_swiboe("/var/run/swiboe.socket")
plugin.register_rpc("buffer.open:gzip", priority = 800, buffer_open_gzip)

# In bzip.py
plugin = connect_to_swiboe("/var/run/swiboe.socket")
plugin.register_rpc("buffer.open:bzip", priority = 799, buffer_open_bzip)

We'll define the buffer_open_gzip function later - buffer_open_bzip follows by analogy.

Some other client (for example the GUI) now wants to open a file:

gui = connect_to_swiboe("/var/run/swiboe.socket")
gui.call_rpc("buffer.open", args = {
   "uri": "file:///tmp/somefile.txt.gz"
})

The args will be converted to JSON and handed to the called function.

The name of the called RPC defines what will get called: Swiboe will find all registered RPCs that have the same prefix, so in our case both buffer.open:gzip and buffer_open:bzip match. The name of the RPCs are really just a convention: dot.separated.namespaces:name_of_implementing_plugin. This allows callers to call to a very specific function:

# I know that somefile.txt.bz is really a gzip file, so call the gzip
# handler directly. Signal it through 'i_know_what_i_am_doing' that we are
# aware that the extension does not match.
gui.call_rpc("buffer.open:gzip", args = {
   "uri": "file:///tmp/somefile.txt.bz",
   "i_know_what_i_am_doing": True,
})

It also allows to call a very broad range of functions:

# Call all buffer functions that start with o - that is not very useful
# probably.
gui.call_rpc("buffer.o", args = {
   "uri": "file:///tmp/somefile.txt.bz",
})

After the functions are determined, Swiboe sorts them by priority and tries them one after the other. The functions work like event handlers in JavaScript or GUIs - they can choose to handle the call and return a value, or they can let the call through to the next. For example, the gzip handler could look like this:

import gzip

def buffer_open_gzip(rpc_context, uri, i_know_what_i_am_doing = False):
   if not uri.endswith(".gz") and not i_know_what_i_am_doing:
      # Let's inform Swiboe that we do not know what to do with this
      # request.
      return rpc_context.finish(NOT_HANDLED)
   file_path = uri[7:]  # Removes file://
   content = gzip.open(file_path).read()

   # Call an RPC to create a new buffer.
   buffer_create_rpc = rpc_context.call_rpc("buffer.create", {
      "content": content,
      "uri": uri,
   })

   # Wait for it to succeed or fail and return this value to our caller.
   return rpc_context.finish(buffer_create_rpc.wait())

Pro: Events are nothing special

This design can also express something like Vim's autocommands - which are just callbacks, really.

A callback is just an ordinary RPC. For example, when the buffer plugin creates a new buffer, it calls on.buffer.created, but does not wait for this call to finish. Everybody interested in buffer creation can just register a matching RPC - with a I_DO_NOT_CARE priority. This signals Swiboe that all these functions can be called in parallel.

If that is not desired, a priority can be specified to decide when to run - if two plugins conflict they can fix their priority values to make things work.

Pro: Extensibility

A plugin is all-mighty in this design. Want to add functionality to buffer.new:core? Just override it with something that has a higher priority and do something else in it. Want to filter out your .gitignore files from fuzzy find? Just write a fuzzy_find.files:git that is called before fuzzy_find:core, calls it directly and filters its results before passing them on. Want to use tab key for every kind of smart content expansion? No problem, just define the order of functions to call and Swiboe will handle it for you.

Everything is a plugin and therefore everything can be changed by other plugins.

Pro: Simplicity

There is only one concept to understand in Swiboe: this layered RPC system. Everything else is expressed through it.

There is a little more to the story - for example RPCs can also stream results. For example the fuzzy files finder uses this to get partial results to the GUI as fast as possible. But the core philosophy is just this one concept.

The big concern: Performance

Swiboe is an RPC system using Unix domain sockets right now. A RPC is more overhead than a simple function call. My benchmark is creating a buffer and immediately deleting it - these are 4 round trips:

client (create request) -> Swiboe
Swiboe (create request) -> buffer plugin
buffer plugin (create response) -> Swiboe
Swiboe (create response) -> client

client (delete request) -> Swiboe
Swiboe (delete request) -> buffer plugin
buffer plugin (delete response) -> Swiboe
Swiboe (delete response) -> client

Quick tangent. This displays well why I choose Swiboe as name. It connects plugins until they found the right partner to talk to, enabling communication with discretion and speed.

This flow takes ~290μs on my system - roughly 500 times slower as doing the same with python function calls. However, I did not try to speed it up in any way yet - both the protocol and the encoding are naive right now.

Also, I believe the RPCs are mainly gluing functionality together. Most computation will be run in the plugins, in separate processes, in parallel. I believe that sufficient performance for a fluent and responsive experience can be reached.

And of course, performance is temporary, design is permanent. So let's start with getting this right first.

Disclaimer

There is a lot of dreaming in this post. There is no python client yet for Swiboe. (Update: no longer true). This design is still subject to change and not completely implemented in Swiboe. There is no gzip or bzip plugin.

Also, the exact design and protocol of the layered RPC system is not written down anywhere yet. (Update: no longer true)

Swiboe is open for contributions! If you want to design a cool text editor with me, feel free to reach out.