Recreating Ulysses with VIM

I like to work on a variety of computers. Only one of them is a non-Apple product. It’s a cheapo Lenovo laptop I got three years ago at Black Friday sale for $300. The original Windows 8 didn’t last much longer than the first battery charge. Now it runs Xubuntu Linux off of a small solid-state drive I installed. Overall, its 11″ screen and poor battery life make it a mostly pathetic specimen soon to relegated to the scrapheap.

But for some reason I enjoy using it. Maybe it’s because of Linux and the availability of software. Or maybe it’s just a small, light machine that is easy to carry. Regardless, I plan to keep it around for a while longer. It may even outlast my MacBook. I also think one of the reasons for my fondness is because of how cheap it was. I don’t have a lot invested so any use I get out of it is a win. Other than the dreadfully short battery life (at best two hours) it’s my go-to machine for when I want to get out of the house.

But doing this also causes a break in my workflow. Since Ulysses is Mac/iOS only I can’t use it on the Lenovo. It’s not a huge burden. Text files swap back-and-forth easily. The major hangup is the aesthetics of the writing environment that Ulysses provides. If you haven’t used it, you are missing out. The simplicity of the layout coupled with the integrated writing goals and built-in file organization make it difficult to be away from. Each time returning to Ulysses I feel a little spoiled at just how nice it feels to have an all-in-one place to do my writing.

As with most things, the convenience comes with a price. And that price is baked into the cost of the Apple hardware. Granted, a low-end iPad Mini can be had for about the price of a cheap laptop and will do 90% of what Ulysses on Mac will do. The rub comes when you need that other 10% for things like working on the guts of a ePub file, or any sort of file conversion (KindleGen, I’m looking at you!). For an independent writer responsible for a farm-to-table writing process, it means at some point you’ll need a laptop/desktop computer to pickup the slack from the iPad. Given a house-fire situation, a cheap laptop and an iPad Mini would be my preferred choice of replacements.

So with that in mind, I set out to see if there was any way I could at least partially replicate the Ulysses experience on Linux. I also want to avoid any “odd” software that might not be actively maintained. I’ve also been a VIM user for a very long time. I know it’s an extremely customizable program, and there are a multitude of plugins to add functionality to it. Because of this my search started with VIM and ended with a set of plugins that make it into a reasonable facsimile of Ulysses.

Before going too deep into details, here’s a list of the plugins I used.

  • VIM (in case you’re unfamiliar with it)
  • Goyo, the writing room plugin
  • Limelight, a paragraph highlighting plugin
  • Vim-markdown, an updated markdown syntax highlighter
  • Fountain.vim, a syntax highlighter for .fountain script files
  • NerdTree, a sidebar file browser
  • Seoul256, one of my favorite themes
  • Vim-fugitive, a plugin that integrates Git into VIM
  • Vim-Plug, an easy-to-use plugin manager

The most amazing part of this is how there’s not really much needed in the way of configuration. All of the plugins have sane defaults, and any tweaking of their settings is just for personal taste.

Initial Setup

If you’ve never used VIM, this might not be the best place to start. There’s a lot of great resources on the web to ease people past the shock of a sitting down with a keyboard-driven text editor like VIM. This one is focused on using VIM for NaNoWriMo, and is a good place to start.

One of the most important files you’ll edit while writing with VIM is the .vimrc settings file (the name means “VIM resource”). This is the file that lives in your home folder and controls all aspects of how VIM behaves. It’s also a source of contention between some VIM users who like to argue about the best configuration. In the interest of avoiding all that, I’ll only list a few general settings that should be used when writing prose rather than code.

.vimrc basics

The following are in no particular order, I’m just going down my .vimrc and pulling examples. These are also some of the more overlooked settings.

set spellsuggest=15

This keeps the spell suggestion window from taking over the whole screen and shows the top fifteen options.

set linebreak

Makes sure lines break on whole words, not in the middle of a word when it meets the screen edge.

set scrolloff=3
set foldcolumn=1

These two work together to keep the cursor away from the edges of the screen. scrolloff keeps at least three lines visible at the top and bottom of the screen unless at the top or bottom of the file. foldcolumn adds one character space to the left edge of the screen. I find this is more pleasant to look at when using VIM normally.

Keybindings and Shortcuts

VIM is all about never having to reach for the mouse. By defining your own shortcuts, it’s infinitely customizable. But the first change you should make is to remap your caps lock key to control. This can be done in the system preferences on a Mac, click the Modifier Keys... button in the Keyboard panel.

In VIM capital letters are different commands than their lowercase brethren. Accidentally hitting caps lock will cause very unexpected results. Plus, on a MacBook the control key is in an awkward place. This change make it easier to reach, as most modifier key commands are based around it. The command key isn’t used.

map j gj
map k gk
map ; :
inoremap jj <esc>
map <space> <PageDown>
" Fix for Ctrl+Space sending NUL
map <NUL> <PageUp>

These are some simple navigation hacks I came up with. Adding the g before j and k makes sure vim moves to the next line on the screen, not just the next full line. Without this, navigating paragraphs of prose would be more trouble than it’s worth. Having ; act line : means there’s no need to press shift to enter command mode. Converting jj to escape avoids your fingers from leaving the home row to exit insert mode. Finally turning the space bar and control+space into page up and page down keys saves hand movement when scrolling.

map <leader>s :setlocal spell<cr>
map <F1> [s
map <F2> z=
map <F3> ]s
map <F4> zg
map <F5> zG

I personally have a hard time remembering the spell checker commands. This batch of shortcuts saves me from that. The <leader> in this case is either the default \ (backslash) or what ever you might have it remapped to. (Note: you don’t press and hold the leader key, just tap it and the tap the next key.) I use , instead, as it’s easier for me to reach. <leader>s starts the spell checker for the current window. Then the F-keys will let me quickly move through the document. F1 searches for the previous misspelling, F3 the next. In between, F2 will bring up the replacement list. F4 adds the word to my personal word list, while F5 adds the word to a temporary list that clears when VIM is closed. So now I can do most of my spell checking with only four keys.

Plugins and their settings

The first plugin I suggest installing is a plugin manager. There are several options, but if you’re not using one I suggest Vim-Plug. The configuration is simple and it can be self-installing, while also automatically install all of your plugins. Simply add the following to the top of your .vimrc:

if empty(glob('~/.vim/autoload/plug.vim'))
  silent !curl -fLo ~/.vim/autoload/plug.vim --create-dirs
  \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
  autocmd VimEnter * PlugInstall | source $MYVIMRC
 endif

What it does: if there’s no ~/.vim/autoload/plug.vim file, it downloads the latest version then runs its install command to install the plugins you’ve specified in your .vimrc. This makes it easy to have a consistent VIM environment no matter what machine you happen to be working on.

As for the plugins you want to install, simply list them between call plug#begin('~/.vim/plugged') and call plug#end() lines. Again, it’s best to have this at the top of your .vimrc. See the Vim-Plug page for more instructions.

Cosmetics

I use Vim-markdown to get the most updated Markdown syntax highlighting, because the version of VIM installed might not be the latest version. Markdown is natively supported in VIM, so if your version is up-to-date, don’t bother. Fountain.vim is for .fountain files, and as the original plugin hasn’t been updated, so I’m hosting it on my Github with pull requests applied.

Seoul256 is entirely optional. It just happens to be my favorite. It’s unique in that it will adjust its tones depending on the background setting. I use let g:seoul256_background=234 most of the time. Make sure to do that before you load the theme. Placing it on the line before colorscheme seoul256 is enough.

The Writer’s Room

The Goyo plugin does the heavy lifting of getting VIM to emulate Ulysses. Its job is to black-out the interface and present a writing area centered on the screen. It’s actually quite elegant.

The only change I made to the default setting was to narrow the sides in some. The default is 80%, I find 65% to match my taste better. So I added let g:goyo_width = 65 to my .vimrc.

One feature I really like about Ulysses is the line highlight option. This draws the eye to where the cursor is. A simple alternative in VIM is the Limelight plugin from the same author. This will darken the non-active paragraphs while leaving the current paragraph undisturbed. When drafting this is quite nice as the focus is completely on the new words. It works without much user intervention, except for the need to activate it each time. Since I only use it with Goyo, I added the following two lines to my .vimrc:

autocmd! User GoyoEnter Limelight0.7
autocmd! User GoyoLeave Limelight!

So when I activate Goyo with <leader>w Limelight switches on. It also switches off when I deactivate Goyo. This makes it pretty much invisible in day-to-day use.

The Sidebar

One of the main attractions of Ulysses is the way it stores your writing. The library keeps track of everything and there’s no need to manage individual files. They have a nice safe place to live and are automatically backed up to iCloud. Once outside of Ulysses, this really isn’t possible. Especially when using multiple operating systems.

One plugin, NerdTree, does offer a workable solution. It’s a sidebar file browser. I have it mapped to Ctrl-N with map <C-n> :NERDTreeToggle<CR> One of NerdTree’s nice features is to have a list of bookmarks for frequently used files and folders. Pressing Shift+b (or capital B) toggles the bookmark list. The command :Bookmark will save the highlighted file or folder to the bookmark list. Also note that the file doesn’t have to be opened, just selected in the NerdTree browser.

Of course, this means file management becomes something requiring thought again. So I’ve found it’s better to just recreate the Ulysses group structure with regular folders. It’s more work, but it is portable across operating systems.

Goals

Sometimes, it seems that the little things are what you miss the most. For me it’s Ulysses’ writing goals. I really enjoy seeing the little circle fill up. There’s not much that can recreate this, especially when you have to use multiple files. But there is a way to at least see the word count of the current document without having to use external tools.

I added a word count function to my .vimrc and have it shown in the status bar.

This is the function and some additional bits to make it work:

let g:word_count="<unknown>"
set updatetime=1000
augroup WordCounter
  au!  CursorHold,CursorHoldI * call UpdateWordCount()
augroup END
function WordCount()
  return g:word_count
endfunction

function UpdateWordCount()
 let lnum = 1
 let n = 0
 while lnum <= line('$')
   let n = n + len(split(getline(lnum)))
   let lnum = lnum + 1
 endwhile
 let g:word_count = n
endfunction

Now, I can use %{WordCount() in my status line. There’s a couple other parts that are needed for it to work. First the status lines needs to be shown. By default it’s not. By setting set laststatus=2 it will be. To show nothing but the word count you can set the statusline variable to something like:

set statusline=%=\ %{WordCount()}

Then you’ll have the word count in the bottom right of your screen. It’s not a writing goal, but it’s better than nothing.

Git for Writers

The replacement of Ulysses library and it’s iCloud/Dropbox syncing and version control can be replaced with Git and Dropbox. The Vim-fugitive plugin makes it possible to do most of the Git version control operation from inside VIM.

While a full discussion of Git & VIM is worth another article, here’s a brief overview.

I have a “bare“ git repository in my Dropbox folder. This is so I can push and pull from it anywhere Dropbox is installed. I would be possible to just work from the Dropbox folder, but I find it more convenient to have my working folder elsewhere. Plus, with Dropbox acting like a server, it’s possible to delete the working folder and not have it deleted from Dropbox. It’s an easy way to add a layer of accident protection to my day.

I also keep my .vimrc and other files in a Git repository, so I can have a consistent VIM environment across computers with a simple git pull.

Wrapping Up

Ulysses is an excellent writing app, with only one bad feature. It’s Apple only. This makes it hard to break away from the Apple-centric ecosystem and use other software. But with a few tweaks, VIM + Git can come close. It’s not a perfect solution, but I’m confident that I could get by with minimal interruption should I need to use commodity hardware.