prism disperses lisp forms (and other languages) into a spectrum of color by depth. It’s similar to rainbow-blocks, but it respects existing non-color face properties, and allows flexible configuration of faces and colors. It also optionally colorizes strings and/or comments by code depth in a similar, customizable way.
One of the benefits of prism is making it easy to see which list elements are in. For example, in this excerpt from org-get-entries-from-diary from org-agenda.el, the funcall’s first argument is an unusually indented if form, and the indentation nearly aligns the funcall’s second argument, date, at the column where the if’s else clause would usually be. But with depth-based colorization, it’s easy to see that date and 1 are arguments to funcall, not part of the if form.
It’s also easy to distinguish the diary-list-entries-hook variable’s value form from other variables, and the entries variable’s different color clearly shows that it has no value form.
It is also useful for non-Lisp languages. For example, here’s an example of JSON in prism-mode:
Here’s an Emacs C function:
It might even help save you from deeply nested, callback-style JavaScript, turning this:
Into this (using theme doom-outrun-electric). Note how the bind is the same color as the function keyword and braces that it corresponds to:
Inspired by paren-face, when the option prism-parens is enabled, parens (any character classified as parenthesis-like syntax by the buffer’s mode) may be colored distinctly from other text, e.g. to make them fade away or stand out. For example, this shows parens being blended into the background with 50% opacity:
And here, at 25%:
In this screenshot, the second and third top-level forms are colorized differently than the first, which points to a programmer error: the first defun’s closing parens are on a line after a comment.
For whitespace-sensitive languages, prism-whitespace-mode determines depth by a combination of indentation and list nesting. For example, Python (showing theme doom-vibrant with these faces set in variable prism-colors: font-lock-type-face, font-lock-function-name-face, font-lock-constant-face, and font-lock-keyword-face):
This example shows Python with prism-comments enabled (showing theme doom-challenger-deep):
Here, even though these if statements’ conditions are parenthesized and split across lines, they are colorized at the same logical depth–and the parts of them in brackets, at a deeper logical depth, are also colorized at the proper depth:
Thanks to Emacs’s mode-specific syntax tables, even complex shell scripts are properly interpreted. In this example, even though the subsequent lines of this shell function are indented more deeply than the first, they are at the same logical depth because of their being continued lines, so they are colorized at the same initial depth, with their parenthesized and bracketed portions colorized at deeper depths (showing theme doom-solarized-dark with a reversed-rainbow palette):
And in this function, even though Emacs indents each part of the the doubly continued line more deeply, they’re colorized with the same color, because they’re at the same logical depth:
It even works in Haskell (showing theme doom-molokai):
It’s easy to adjust the colors with prism-set-colors. Here are some examples.
You can use just a few faces in combination with the desaturations and lightens to create a palette of colors:
Or even a single color, going in one direction:
…or the other:
The default configuration looks decent in the default Emacs theme:
If you use Doom themes, you can use doom-color to get colors from the theme:
But some of them look nice without any customization, like doom-gruvbox:
If you use solarized-theme, you can use solarized-with-color-variables to get colors from the theme:
And you can adjust the palette extensively by changing the applied desaturation and lightening:
You can shuffle the order of the colors until you find a pattern you like:
You can even set themes buffer-locally (the theme-choosing command shown here is not included, but you can easily define your own “chooser” command using unpackaged/define-chooser):
prism is much like rainbow-blocks, but it differs in a few ways:
prismoptionally colorizes comments and strings according to the depth of their surrounding code.prismhighlights parens with the color of the outer list’s symbols, which helps parens stand out from symbols and shows which depth surrounds a list.prismadds to thefacetext property, which respects existing fontification, whilerainbow-blockssets thefont-lock-facetext property, which overrides existing fontification. This means thatprismis compatible with packages like highlight-function-calls and highlight-quoted.prismusesfont-lock-add-keywords, whilerainbow-blocksusesjit-lock-register. Which is better? Good question. Hopefully, the former…
The easiest way is to use quelpa-use-package like this:
(use-package prism
:quelpa (prism :fetcher github :repo "alphapapa/prism.el"))- Run the appropriate command for the current buffer:
- For Lisp and C-like languages, use
prism-mode. - For significant-whitespace languages like Python, or ones whose depth is not always indicated by parenthetical characters, like shell, use
prism-whitespace-modeinstead.
- For Lisp and C-like languages, use
- Enjoy.
- If the colors aren’t satisfactory, use command
prism-randomize-colorsto randomize theprismcolors according to the current Emacs theme. When you find a set you like, you may save the colors with commandprism-save-colors. - When a theme is loaded or disabled, and
prism-colorsis a list of faces (rather than a list of colors),prism-colorsis automatically updated. Ifprism-colorsis a list of colors, callprism-set-colorsorprism-randomize-colorsmanually to update for a new theme. - To customize, see the
prismcustomization group, e.g. by using M-x customize-group RET prism RET. For example, by default, comments and strings are colorized according to depth, similarly to code, but this can be disabled.
More advanced customization of faces is done by calling prism-set-colors, which can override the default settings and perform additional color manipulations. The primary argument is COLORS, which should be a list of colors, each of which may be a name, a hex RGB string, or a face name (of which the foreground color is used). Note that the list of colors need not be as long as the number of faces that’s actually set (e.g. the default is 16 faces), because the colors are automatically repeated and adjusted as necessary.
Faces may be remapped buffer-locally by setting the LOCAL argument to t (interactively, with one universal prefix); if set to reset (interactively, with two prefixes), local remappings are cleared.
If prism-set-colors is called with the SAVE argument, the results are saved to customization options so that prism-mode will use those colors by default.
Here’s an example that the author finds pleasant (seen in the first screenshot):
(prism-set-colors :num 16
:desaturations (cl-loop for i from 0 below 16
collect (* i 2.5))
:lightens (cl-loop for i from 0 below 16
collect (* i 2.5))
:colors (list "dodgerblue" "medium sea green" "sandy brown")
:comments-fn
(lambda (color)
(prism-blend color
(face-attribute 'font-lock-comment-face :foreground) 0.25))
:strings-fn
(lambda (color)
(prism-blend color "white" 0.5)))Additions
- Option
prism-whitespace-mode-indentsincludes a default foryaml-modebuffers.
Changes
- Command
prism-randomize-colorsmore consistently produces the desired number of colors. (However, it is still limited by the element of chance and the available faces; it may take several iterations to find a pleasing set of colors in a given circumstance.)
Fixes
- “Invalid search bound (wrong side of point)” error. (#27. Thanks to Ketan Kanishka for reporting.)
- Face applied within sequences at top-level indentation in
prism-whitespace-mode(e.g. in Python, the arguments in a top-level function definition).
Fixes
- Further fix to customization setter.
Fixes
- Customization setter could fail at load time.
Compatibility
- Depend on Emacs 27.1 or later. (This was actually needed since v0.3.2, but was overlooked then.)
Fixes
- Infinite loop in
prism-matchwhen a double-prefixed comment appears next to a non-comment, non-whitespace character. (#26. Thanks to Daniel Neal for reporting.) - Call
face-attributewith itsinheritargument specified asdefault. (Fixes #22. Thanks to Bram Schoenmakers for reporting.)
Fixed
- When
prism-commentsis enabled in major modes whose syntax tables do not allow searching for comment delimiters. (Related to #18.) (Thanks to Jason Zhen for reporting.)
Added
- Failsafe to prevent bugs from causing infinte loops in Emacs.
Fixed
- Infinite loop with certain buffer contents. (Further fixes #18.) (Thanks to Jason Zhen and fkr-0 for reporting.)
Added
- Option
prism-parens, which allows parenthesis characters (by syntax type, not only( )) to be colorized differently (e.g. to make them fade away or stand out). The functionprism-set-colors’s new argument,parens-fn, defaults to one which fades parens into the background (which only applies when the option is enabled).
Changed
- Both
prism-modeandprism-whitespace-modedeactivate the other mode when activated, allowing them to be switched between without having to disable one first.
Fixed
- Code comments in strings (or what appeared to be, e.g.
"Foo; bar"in Lisp) were fontified as comments rather than strings. - End-of-buffer errors signaled in
font-lock-fontify-keywords-region. (Fixes #6. With thanks to @vuori and @piknik.) - Call
font-lock-flushwhen disabling modes. (Thanks to Joseph Turner for reporting.) - Infinite loop with certain buffer contents (involving strings directly adjacent to other tokens). (Fixes #18. Thanks to @nathanvy for reporting.)
Fixed
- Depth of logically continued lines (e.g. in Python, an expression split across lines) and physically continued lines (e.g. in Shell, a statement split across backslash-continued lines) in
prism-whitespace-mode.
Fixed
- The fix in previous version. Oops.
Fixed
- Ignore faces with
unspecified-colors (e.g. when used in a terminal).
Added
- Command
prism-randomize-colors, which setsfacesbased on a random, shuffled selection offont-lockfaces in the current Emacs theme.
Fixed
- Performance issues with large Lisp forms.
First tagged version. Possibly a few sneaky bugs lurking, but seems to work well.
Inspired by rainbow-blocks, rainbow-identifiers, and rainbow-delimiters.
Bug reports, feature requests, suggestions — oh my!
In the event that a bug in the font-locking functions cause Emacs to enter an infinite loop, you can stop it without killing Emacs by following these steps:
- From a shell, run
pkill -SIGUSR2 emacs. Usually once is enough, but not always. - After Emacs displays a backtrace, switch to the buffer where
prism-modewas enabled and callprism-modeagain to disable it. - Please report the backtrace to the issue tracker so it can be fixed. Include contents of the buffer when possible.
GPLv3