Better Markdown folding in BBEdit

One of the few things I don’t like about BBEdit’s Markdown language module is the way it handles section folding. Here’s a simple example document, similar to the format I use when writing a report:

Title: A simple report in Markdown
Author: Dr. Drang
Date: June 11, 2014
Client: Markdown Industries, LLC
        1029 Race St
        Philadelphia, PA 19107
Format: complete

# Introduction #

Most unexpected nature, will leave most progeny. But in many
cases how to rank as representative species; and not others
to enter, on this principle nurserymen always prefer saving
seed.

# Section 1 #

Has been erroneously asserted that the good effects of
intercrossing, we shall presently see that simple
inheritance often gives the length of the ears and tails
others singularly short tails. The barb pouter, and fantail
pigeons. were destroyed, chiefly by slugs and insects. If it
could not, in the least reason to doubt that the love of man
driven up and surviving till one hundred years and the mass
when upraised will give an explanation of.

## Subsection 1.1 ##

Lowlands, would again be preserved, and accumulated by
natural selection on the other hand, the points of
difference. between the different castes of neuters in the
form and.

### Subsubsection 1.1.1 ###

Throughout the world on very small From these several
considerations I will not, however, assume that the seeds
might be transported across a wide area, and to
naturalisation--Action of sexual selection that is, two
individuals ever concur in reproduction? and in others they
use it as of the mimicked and others as distinct, as
carriers, tumblers, pouters, and fantails now are, we to
marvel if all the species of bats, animals which can be
dimly seen or.

### Subsubsection 1.1.2 ###

Of the world should have all produced an analogous
explanation has been slurred over, owing to few animals now
generally resist with undiminished fertility repeated
changes of conditions, for the inhabitants of.

## Subsection 1.2 ##

Circumstances their rate of increase is proved by the
enormous plates of baleen, as in the family of shepherd-dogs
I cannot see that there is no real objection to this
conclusion, The case of varieties, Descent always used in
classing varieties: I apprehend in every shade and stripe of
colour appears from what we know to escape from beasts of
prey.

# Section 2 #

For a long period and of stronger ones in the polity of
nature: hence in the diagram by the letter F14. All the many
existing oceanic islands. Several facts in the distribution
of this archipelago. The Bonin Islands, the Caroline and
Marianne Archipelagoes, and Mauritius, all possess their own
peculiar species of Cypridina has well-developed organs of
locomotion or of the.

Normally I use soft wrapping, but to make it fit here, I’ve hard-wrapped the lines at 60 characters.

In BBEdit, the heading lines have little triangles out in the gutter. These are both an indication of where folds can be made and the button that does the folding when clicked.

BBEdit Markdown unfolded

What I don’t like is where the folding stops. If, for example, we click on the triangle next to Section 1, the text that gets folded stops just above Subsection 1.1.

BBEdit Markdown native folding

In other words, BBEdit believes that folding should extend only to the next heading, whatever the level of that heading.

I take a different view. To me, this is a structured document, and the heading levels should determine how the text gets folded. When I fold at Section 1, I want all of Section 1 to disappear, including the sub- and subsubsections.

BBEdit Markdown smarter folding

So I wrote an AppleScript and Keyboard Maestro macro to fold the way I want. The logic works like this:

  1. It searches up from the current cursor location to the nearest header line. This will be the top of the fold.
  2. It counts the number of hashes (#) to determine the level of the header.
  3. It then searches down until it finds either

    • A header of the same level.
    • A header of a higher level, where by “higher,” I mean higher in the document hierarchy. A level 1 header is higher than a level 2 header.
    • The end of the document.

    Whichever comes first will be the bottom of the fold.

  4. It then folds the text between those two positions.

Here it is in the Keyboard Maestro editor:

Keyboard Maestro folding macro

The guts of the macro is this AppleScript, which finds the top and bottom of the fold and selects all the text between.

applescript:
tell application "BBEdit"
  activate
  tell text of front document
    set thisHeader to find "^#+" options {backwards:true, search mode:grep} with selecting match
    if found of thisHeader then
      set level to length of found text of thisHeader
      set endHeader to find " *#*$" options {search mode:grep}
      set startFold to characterOffset of found object of endHeader
      set regex to "^#{1," & level & "} "
      set nextHeader to find regex options {search mode:grep} with selecting match
      if found of nextHeader then
        set endSection to find "\\S" options {search mode:grep, backwards:true}
        set endFold to (characterOffset of found object of endSection)
      else
        set endFold to characterOffset of last character
      end if
      select (characters startFold thru endFold)
    end if
  end tell
end tell

I have a feeling this script isn’t the most robust in the world and will break if the document’s format differs significantly from what I typically use. I’ll probably have to make adjustments to the find commands as I get more experience with it.

(You might be wondering why I bothered with Keyboard Maestro—after all, AppleScript can select menu items and move the cursor. Originally, the AppleScript did everything, handling the menu click through System Events calls. Unfortunately, while this worked perfectly when the script was run from the AppleScript Editor, it failed when run from within BBEdit. I recalled running into something like this before, but I didn’t remember what the solution was, so I just used Keyboard Maestro for the UI bits at the end. Pragmatism over elegance.)

To use the macro, I put the cursor near the top of a section or subsection and hit ⌃⌥⌘S. The exact position of the cursor isn’t important; it just has to be above any enclosed subsections. Although the example I gave above folded a top-level section, the macro can fold at any level. Here’s what happens when I invoke it with the cursor just below the Subsection 1.1 line:

BBEdit Markdown smarter folding 2

The downside of using non-native folding like this is that the little triangles don’t work. In fact, as you can see, they disappear when the text is folded. I have to double-click the elipsis button to expand the folded text. No matter. I now have a folding system that thinks about the document the same way I do.