Fanael/rainbow-delimiters — pull request #1 by Fanael (2014-10-27 23:35:42+00:00)
Title: Ignore blacklisted delimiters altogether.

Blacklisted delimiters don't contribute to depth anymore.

This was [suggested by a user](https://github.com/jlr/rainbow-delimiters/issues/6#issuecomment-14724703) some time ago.

===

In theory, ignoring blacklisted delimiters is a very simple operation. You just need to modify the syntax table so they're not delimiters, so `syntax-ppss` doesn't count them when calculating depth.

Okay, I lied. It's not that simple. You see, the buffer-local syntax table is not the only place syntax table entries can come from. Text properties and overlays are cromulent sources of syntax table entries, too. Even ignoring the, admittedly ridiculous, idea that the major mode and other minor modes are messing with us by changing text properties on random characters, it's *much* harder than modifying the syntax table. Besides, we *only just stopped modifying the syntax table*.

Let's assume we've dealt with syntax entries somehow. We're done now, aren't we? After all, determining the depth is just a `(syntax-ppss)` call away, right? **No**. `syntax-ppss` is reasonably fast because it uses a cache internally. If you change the syntax in any way without telling `syntax-ppss` about it, it will use its cache, and that cache *will* contain stale entries from before the syntax change.

Okay, so we can't use `syntax-ppss` without telling it to drop the caches. So maybe, I don't know, tell it to drop the caches? `(syntax-ppss-flush-cache 0)` is not exactly rocket science, is it? As it happens, there's exactly *one* call to `syntax-ppss`, so dropping the caches not only will turn that call into `(parse-partial-sexp (point-min) pos)`, it also will slow down all the code calling `(syntax-ppss)`, because after `rainbow-delimiters--propertize` is done, the cache has to be dropped again, pretty much turning *all the calls to `syntax-ppss` into `parse-partial-sexp` from `(point-min)`*.

If that's the case, would building our own private cache around `parse-partial-sexp` be a good idea? Maybe. But it duplicates existing code, and there's really nothing wrong with `syntax-ppss`. Besides, we *only just stopped doing that*, too.

So, what does this patch do?

===

I feel filthy having created this atrocity. This patch is made 100% of completely irredeemable pure evil. And it's **not** the nice kind of evil that turns Emacs into a decent editor. No, it's the evil that murders puppies just for the hell of it. Why, you ask?

* It hooks into `syntax-propertize-function` and *calls* the original function. Why is this chaining bad? This comment, found right in the middle of the `syntax-propertize-function` definition, explains it well:
```emacs-lisp
;; Rather than a -functions hook, this is a -function because it's easier
;; to do a single scan than several scans: with multiple scans, one cannot
;; assume that the text before point has been propertized, so syntax-ppss
;; gives unreliable results (and stores them in its cache to boot, so we'd
;; have to flush that cache between each function, and we couldn't use
;; syntax-ppss-flush-cache since that would not only flush the cache but also
;; reset syntax-propertize--done which should not be done in this case).
```
* It uses the `category` text property. Never heard of it? I'm not surprised, me too, until when I saw `cc-mode` using it when I was writing the rainbow-delimiters test suite. `category` is a text property indirection. When you put a `category` on some text, you can affect that text's effective properties by changing the `category`'s symbol plist. Spooky action at a distance? You bet.
* It cracks open the skull of `syntax-ppss`, removes its brain, replaces it with a different brain, does some work and then gives `syntax-ppss` its old brain again. That's the entire point of `rainbow-delimiters--with-syntax-ppss-cache`. It lets us use `syntax-ppss` without worrying about its cache: the one rainbow-delimiters sees is separate from the one seen by everyone else.
* It doesn't clean up after itself very well. Even if you change the *major mode*, the categories are still there. Clearly something more dynamic than `syntax-propertize-function` is needed? There's a thing dynamic just enough: `font-lock-mode`. I shudder at the thought of a font-lock entry, and one run late at that, *and* one managed by a *major-mode-oblivious minor mode* on top of that, managing *syntactic* information.

The underlying idea, though, is pretty simple. `font-lock-mode` always ensures the syntactic properties are set before highlighting, so we register a `syntax-propertize-function` that finds all eligible blacklisted delimiters and puts a, inert at the moment, `category` on them. Later, inside `rainbow-delimiters--propertize`, we temporarily modify the syntax codes (reverting it when `rainbow-delimiters--propertize` is done with its job) by setting the `category`'s `syntax-table` to `'(1)`. As per [(elisp)Syntax Table Internals.](https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Table-Internals.html), that's punctuation. `syntax-ppss` sees this and treats the would-be delimiter as any other punctuation.

The idea is okay. The implementation I'm utterly disgusted with.

@purcell: where do I turn in my Emacs Lisp hacker badge?

===

As it is, this patch and being mergeable are not even in the same galaxy. It's a quickly hacked together proof of concept. It may be a decent starting point, though.