5

I am writing a font-lock rule that calls a function to decide what face to apply to a symbol. Here's a simplified version:

("\\(?1:\\s_+\\)"
 (1 (the-function 1)))

The problem is that this function needs to generate some data that is just a little bit slow to compute. So, when jit-font-lock starts calling this function for every symbol in the visible buffer, the lag is noticeable.

I could setq this “slightly-expensive” data to a variable,

(setq data-cache (generate-data))

and then the-function could just use data-cache instead of calling generate-data every time. But then I would have to jump through some non-trivial hoops to make sure that this cache is always properly invalidated.

Q: Does the font-lock engine offer help in this regard? How can I make sure it only runs (generate-data) once per redisplay?

Ideally, I would like to specify something like “let-bind data-cache to the return value of (generate-data). This way I would know that this cache was never outdated.

Malabarba
  • 22,878
  • 6
  • 78
  • 163
  • When should the cache be invalidated? (Or IOW, what does the return value of `(generate-data)` depend on?) – François Févotte Sep 05 '15 at 18:47
  • @Francesco The cache does **not** need to be invalidated while font-lock is doing its thing (which is why I was hoping font-lock could let-bind it for me). Technically, it needs to be invalidated before each user command, though I can think of other ways it could be done. The return value of `generate-data` depends on output from an asynchronous background process, and it affects the way that we handle user commands. – Malabarba Sep 05 '15 at 18:55
  • You could perhaps use font-lock's indirection mechanism, setting `font-lock-fontify-region-function` to a function which let-binds `data-cache` around the call to the prior value of `font-lock-fontify-region-function`. Sort of like an around-advice, except the indirection mechanism allows you to do it in a cleaner way. – François Févotte Sep 05 '15 at 19:12
  • @Francesco That's interesting. I'll give it a try! – Malabarba Sep 05 '15 at 19:24
  • @Francesco Yep, it works. Would you like to make that an answer? – Malabarba Sep 06 '15 at 13:21

2 Answers2

4

You could perhaps use font-lock's indirection mechanism, setting font-lock-fontify-region-function to a function which let-binds data-cache around the call to the prior value of font-lock-fontify-region-function. Sort of like an around-advice, except the indirection mechanism allows you to do it in a cleaner way.

François Févotte
  • 5,917
  • 1
  • 24
  • 37
0

Elisp has closures now. Something like this should work:

;; -*- lexical-binding: t -*-    
(let ((list (let (cache)
              (list (lambda ()
                      (unless cache
                        (message "setting cache")
                        (setq cache 42))
                      cache)
                    (lambda ()
                      (message "invalidating cache")
                      (setq cache nil))))))
  (fset 'do-stuff (car list))
  (fset 'invalidate-cache (cadr list)))

(add-hook 'post-command-hook #'invalidate-cache)