Skip to content

Parameterize color schemes #41

Description

@alysbrooks

Currently, colors are hard-coded, but there are various situations where you might want to set different colors:

  • To ensure adequate contrast on a different background
  • To ensure readability by people who are colorblind
  • To match your editor or main diff tool's color scheme.

In addition to general usefulness, parameterizing color schemes may address #14 and would be required for lambdaisland/kaocha#403.

  • Allow for multiple built-in schemes: at least dark and light, and possibly a colorblind friendly scheme (if we can't make the dark and light schemes adequately colorblind) or a "classic" scheme if we end up tweaking colors.
  • Allow for customization of the scheme

Activity

  1. moved this from Triage to Candidate in Lambda Island Open Sourceon Mar 24, 2023
  2. humorless commented on Apr 5, 2023

    @humorless
    Member

    Hi @alysbrooks ,

    From what I know about deep-diff2, the color is mostly controlled by here.
    For example, when green is chosen for a string "hello", it outputs \u001b[32m hello \u001b[0m. Here, green color is replaced to color digit 32 through the sgr-code hashmap.

    How you do think about the following implementation?

    • I implement a new namespace under color directory, like eight-bits-color.clj
    • Similar to ansi.clj, but support 256 colors.
    • Similar to ansi.clj, but it accept the user to inject a new map to replace sgr-code.
  3. humorless commented on Apr 6, 2023

    @humorless
    Member

    @alysbrooks @plexus

    I made some experiments. Actually, I can change the color to even 256 color without changing any single line of code.

    For example, if I change the :lambdaisland.deep-diff2.printer-impl/deletion from [:red] to [:bg-256 5 13], the color code it outputs will change from \u001b[31m to \u001b[48;5;13m

    Screen shot
    截圖 2023-04-06 下午5 25 45

    user=> (use 'lambdaisland.deep-diff2)
    nil
    user=> (def color-printer (printer {:color-scheme {:lambdaisland.deep-diff2.printer-impl/deletion [:bg-256 5 13]}}))
    #'user/color-printer
    user=> (pretty-print (diff {:a 1} {:b 2}) color-printer)
    {+:b 2, -:a 1}
    
  4. humorless commented on Apr 6, 2023

    @humorless
    Member

    Oh, after reading related issues more carefully, I find out that there are ansi.clj can controll more than I thought.

  5. plexus commented on Apr 6, 2023

    @plexus
    Member

    So it seems that Puget already allows customization through a custom printer? In that case I think there's not much more that needs to happen here, and it's more a matter of exposing this configuration in Kaocha's tests.edn.

    Note that not all terminals support full RGB colors, although it's relatively widely supported these days. The downside of RGB colors is that, because they are absolute, you basically have to also set the background, because you can't predict what contrast you'll get. Whereas with the basic 16(?) colors it's up to the terminal how they render that exactly, so the terminal's color scheme should ensure that there's a sensible amount of contrast.

    Which kind of goes back to the original issue, since we use these basic built-in colors ("red", "green"), rather than fully specified RGB colors, it's really up to the terminal color scheme to make sure they render ok on the default background.

  6. alysbrooks commented on Apr 7, 2023

    @alysbrooks
    MemberAuthor

    Reading through the code and this discussion, I think we should take the following steps:

    1. Document how to set up a custom printer
    2. Optionally, create a helper function to make it easier to set custom named colors or RGB colors. lambdaisland.deep-diff2.printer-impl/puget-printer isn't that complicated, but there's quite a bit of set up that's orthogonal. It would be nice if you could do (deep-diff/custom-printer {:deleted :red :inserted [100 250 0]}), for example.
    3. Expose configuration in Kaocha.

    I'm not sure my idea for color schemes really makes sense for deep-diff anymore. Perhaps a library or community extension would be more appropriate for that sort of thing. I think it would make sense in Kaocha (perhaps as a plugin). The one feature that might make sense in deep-diff2 is a fallback option, where we set the foreground colors and background colors to ensure good contrast so people can have something readable if their terminal color scheme isn't ideal or has gotten messed up somehow.

    I think @humorless and I could split these among ourselves, unless you have time/interest, @plexus?

  7. plexus commented on Apr 10, 2023

    @plexus
    Member

    Sounds good, you can go for it.

  8. self-assigned this
    on Apr 14, 2023
  9. moved this from Candidate to 🏗 In progress in Lambda Island Open Sourceon Apr 14, 2023
  10. moved this from 🏗 In progress to 👀 In review in Lambda Island Open Sourceon Apr 14, 2023
  11. moved this from 👀 In review to ✅ Done in Lambda Island Open Sourceon Apr 15, 2023
  12. lread commented on May 30, 2023

    @lread
    Contributor

    This is a nice compromise for #14, thanks @alysbrooks and @humorless! I just used the updated docs to change deletion and insertion highlighting which made it much easier for me to see diffs in a large data structure. ❤️

  13. metametadata commented on Jan 4, 2026

    @metametadata
    user=> (use 'lambdaisland.deep-diff2)
    nil
    user=> (def color-printer (printer {:color-scheme {:lambdaisland.deep-diff2.printer-impl/deletion [:bg-256 5 13]}}))
    #'user/color-printer
    user=> (pretty-print (diff {:a 1} {:b 2}) color-printer)
    {+:b 2, -:a 1}

    This example from README unexpectedly resets all the other default :color-scheme values. So for example, insertions will not be highlighted anymore.

    The cause is in how opts are merged with the defaults in lambdaisland.deep-diff2.printer-impl/puget-printer.


    It's also surprising to propose using lambdaisland.deep-diff2.printer-impl as it's an internal namespace.

  14. humorless commented on Jan 5, 2026

    @humorless
    Member

    @metametadata

    Based on your points, I would propose to fix this like this:

    1. Logic Level: Use update + merge
      Instead of performing a top-level merge on the entire opts map, the function should specifically merge the inner :color-scheme map. This prevents the user's input from wiping out the default color keys.
    (defn puget-printer
      ([] (puget-printer {}))
      ([opts]
       (let [extra-handlers (:extra-handlers opts)
             ;; 1. Define the default color scheme
             default-colors {::deletion  [:red]
                             ::insertion [:green]
                             ::other     [:yellow]
                             :boolean    [:bold :cyan]
                             :tag        [:magenta]}
             ;; 2. Merge user-provided colors into the defaults
             user-colors    (:color-scheme opts)
             merged-colors  (merge default-colors user-colors)]
         
         (puget-printer/pretty-printer 
          (merge {:width           (or *print-length* 100)
                  :print-color     true
                  :color-scheme    merged-colors ; Use the merged result
                  :print-handlers  (dispatch/chained-lookup
                                    (print-handler-resolver extra-handlers)
                                    puget-printer/common-handlers)}
                 ;; Dissoc :color-scheme here so it doesn't overwrite the merged version
                 (dissoc opts :extra-handlers :color-scheme))))))
    
    1. Architecture Level: Simplify the API Keywords

    To prevent users from having to reference lambdaisland.deep-diff2.printer-impl/deletion, the library should provide a mapping layer. This allows users to pass simple keywords like :deletion, which are then translated to the internal namespaced keys.

    (defn- normalize-colors
      "Translates user-friendly keys to internal namespaced keys."
      [colors]
      (let [mapping {:deletion  ::deletion
                     :insertion ::insertion
                     :other     ::other}]
        (reduce-kv (fn [m k v]
                     (assoc m (get mapping k k) v)) ;; Fallback to original key if not in mapping
                   {}
                   colors)))
    
    ;; Inside puget-printer:
    (let [user-colors   (normalize-colors (:color-scheme opts))
          merged-colors (merge default-colors user-colors)]
      ...)
    
  15. metametadata commented on Jan 6, 2026

    @metametadata
    1. This looks like re-implementation of what the existing helper lambdaisland.deep-diff2.puget.printer/merge-options does a bit later in puget-printer/pretty-printer. So maybe everything can be simplified somehow, taking that into account 🤷

    2. I'd avoid introducing "user-friendly" keys (as opposed to "internal" ones). Because additional terminology will make it harder to grasp the codebase.

      Considering the only problem with lambdaisland.deep-diff2.printer-impl/deletion is "impl" part, I'd simply rename these keys everywhere to :deletion (or maybe ::lambdaisland.deep-diff2/deletion to avoid any potential clashes). An then if you want to keep backwards-compatibility (and avoid bumping library major version) then support for the old names will be needed, as in the proposed mapping function.

  16. humorless commented on Jan 14, 2026

    @humorless
    Member
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions