cider icon indicating copy to clipboard operation
cider copied to clipboard

Introduce `custom-face-1`/`custom-face-2` facility

Open vemv opened this issue 1 year ago • 6 comments

Context

DSLs and libraries with special semantics aren't uncommon in the Clojure world.

Users of those may want some extra highlighting rules, with different faces, that apply to just those DSLs.

Those extra faces allow to:

  • distinguish "normal" Clojure from DSL code
    • note that both can be interleaved
  • make that DSL simply nicer to read in itself

CIDER by default gives a relatively small palette of faces to be possibly applied, so stuff either won't get any particular font-locking, or the applied font-locking will overlap with Clojure font-locking (e.g. N different things all use font-lock-variable-name-face), hindering the desired distinction.

Changes

Introduce two new faces and two customizable 'categorizers' that when set, decide whether a given symbol should use one of the custom faces.

Example

The user would simply use something like:

(setq cider-custom-symbol-categorizer-1 (lambda (sym meta)
                                          (member (nrepl-dict-get meta "ns")
                                                  '("com.rpl.ramaspecter"))))

(setq cider-custom-symbol-categorizer-2 (lambda (sym meta)
                                          (member sym
                                                  '("<<sources"
                                                    "<<if"
                                                    "<<subsource"
                                                    "<<cond"
                                                    "<<ramaop"
                                                    "<<ramafn"
                                                    "or>"
                                                    "and>"
                                                    "else>"
                                                    "case>"
                                                    "<<shadowif"))))

And then one's theme can be customized to use the produced faces:

(custom-theme-set-faces
 'my-theme
 `(cider-custom-face-1 ((t (:foreground "blue"))))
 `(cider-custom-face-2 ((t (:foreground "red")))))

Status

Generally ready and functional, except for the addition of a user manual + changelog entry.

I also plan to provide a little more metadata from cider-nrepl (in an efficient manner).

Tweaks welcome.

Cheers - V

vemv avatar Apr 19 '24 17:04 vemv

I'll need some specific use-cases to be able to evaluate the usefulness of this proposal as DSLs come in many flavors and I don't see immediately how a couple of extra faces will make a big difference for them.

Might also be useful to have this as some generic mechanism - e.g. some macro that will define a matching face, instead of hardcoding some faces. Admittedly this will make theming a more manual process, but I doubt that will be a big deal for most people.

bbatsov avatar Apr 20 '24 18:04 bbatsov

As for examples:

  • Rama has a dataflow DSL - it's code that is embedded into normal Clojure which however runs in a distributed fashion.
    • So, the highlighting makes it easier to see what is being run as normal code, and what is being distributed.
  • Electric has functions that run on the client, and that run in the server, interspersed
  • Specter has a navigator DSL which users might want to highlight distinctively
    • e.g. the all-caps part in (transform [MAP-VALS ALL MAP-VALS even?] inc data)
    • this is not unlike highlighting for SQL - distinguishing a query part from a code part makes things more comprehensible, to some.

Might also be useful to have this as some generic mechanism - e.g. some macro that will define a matching face, instead of hardcoding some faces. Admittedly this will make theming a more manual process, but I doubt that will be a big deal for most people.

I could use some pseudo-code to fully grasp the idea.

vemv avatar Apr 20 '24 19:04 vemv

Something like:

(cider-define-font-locking-category 'category-name 
                                          (lambda (sym meta)
                                          (member (nrepl-dict-get meta "ns")
                                                  '("com.rpl.ramaspecter"))))

And this would create some face based on category-name. Or we can specify the face explicitly. Probably this should also take some face specification. And that's what I said that theming becomes a bit tricky - you'd need to have face overrides only after some faces have been created, but that's not that big of deal IMO.

bbatsov avatar Apr 21 '24 10:04 bbatsov

Can give that a shot - would seem nice as I wasn't sure of how many 'slots' we should offer.

I take it that we'd go ahead with that?

vemv avatar Apr 21 '24 13:04 vemv

Yeah, if it's flexible I'm OK with it.

bbatsov avatar Apr 21 '24 14:04 bbatsov