Learn Vim at your own pace with my self-study Core Vim Course.

Learn more

Learn Vim at your own pace with my self-study Core Vim Course.

Writing a custom fold expression


Run time:

With a little bit of Vimscript, you can create a custom folding expression for any filetype. We’ll start by looking at the mechanics of folding with markers, then go on to create a folding expression for markdown documents.


Here’s a gist of the markdown folding expression that was created in the video.

Creating an ftplugin

Create a filetype plugin for markdown files as follows:

mkdir -p ~/.vim/after/ftplugin/markdown
vim ~/.vim/after/ftplugin/markdown/folding.vim

You can replace markdown with the name of any other language to create a filetype plugin for that language instead.

The mechanics of a foldexpr

Here’s the boilerplate fold expression that we used to begin with:

function! MarkdownFolds()
  return "0"
setlocal foldmethod=expr
setlocal foldexpr=MarkdownFolds()

It works like this: the MarkdownFolds() function is called one time for each line in the document. If the function returns "0", it indicates that the line is not part of a fold. If the function returns "1", it indicates that the line has a fold level of one. Here are a few more values that can be returned from a fold expression, with their meanings:

value meaning
"0" the line is not in a fold
"1", "2", ... the line is in a fold with this level
"=" use fold level from the previous line
">1", ">2" a fold with this level starts at this line

For a complete list of values that can be returned by a fold expression, look up :help fold-expr.

Inside of a fold expression, we can use the v:lnum variable to access the line number of the current line that is being evaluated. This can easily be combined with the getline() function to get the contents of the current line that’s being evaluated.

Customizing the foldtext

We can customize the way that a fold looks by way of the foldtext option. Here’s the boilerplate foldtext expression that we used to begin with:

function! MarkdownFoldText()
  return getline(v:foldstart)
setlocal foldtext=MarkdownFoldText()

Inside of a foldtext expression, we can use the v:foldstart and v:foldend variables, which reference the line numbers of the first and last lines that make up the current fold.

Further Reading

In preparing this material, I took inspiration from several sources, including:


Level-up your Vim


Boost your productivity with a Vim training class. Join a public class, or book a private session for your team.

Drew hosted a private Vim session for the shopify team that was one of the best workshops I have ever attended.

John Duff, Director of Engineering at Shopify


Make yourself a faster and more efficient developer with the help of these publications, including Practical Vim (Pragmatic Bookshelf 2012), which has over 50 five-star reviews on Amazon.

After reading it, I've switched to vim as my default editor on a daily basis with no regrets. ★★★★★

Javier Collado

Learn to use Vim efficiently in your Ruby projects

In association with thoughtbot, one of the most well respected Rails consultancies in the world, I've produced a series of screencasts on how to make navigating your Ruby projects with Vim ultra-efficient. Along the way, you’ll also learn how to make Ruby blocks a first-class text object in Vim. This lets you edit Ruby code at a higher level of abstraction. Available to buy from thoughtbot..