The new LanguagePatterns class lets us easily configure the patterns for
a language in a similar way to the original plugin. Most language
features can be configured by passing strings to the alternate
constructor. And the overloaded + operator makes combining patterns easy.
findMatchingPair() was refactored to work with the new class.
In addition, the concept of prefixes was removed. The cursor should jump
if it's anywhere inside or before an extended pair (excluding the
default pairs). Instead of appending a prefix to each regex, we simply
check in findMatchingPair() what the closest pair to the cursor is. The
original plugin behaves the same way.
We prefer matches containing the cursor over matches after the cursor.
If the cursor in inside multiple patterns, we pick the smaller one. And
a default pair after the cursor is preferred over any extended pairs
after the cursor.
The ending offset of the initial match isn't enough information for some
language features. For example, if the cursor is on "i" in the "else if"
of a Makefile, the previous implementation would jump to the "else" on a
reverse jump instead of treating "else if" as a single structure.
parsePatternAtOffset() needs to return both the match start and end for
us to correctly handle such a distinction.
findClosingPair() always moves forward and findOpeningPair() always
moves backwards. That fact lets us simplify both implementations.
Using a stack of Pairs to track the match starts and ends is unnecessary
since we know ahead of time whether the jump needs to go to the start or
end of the target pair.
findOpeningPair() can be further simplified since it doesn't need to
check the isInOpPending flag -- the distinction between operator pending
mode and regular jumps only matters when moving forward in the buffer.
We need to set newend after the content of match is final, otherwise we
can end up in an endless loop. This allows us to remove the
startoff != latestOff check that previously prevented this endless
loop and caused this bug.