1. Overview
Canon replaces invisible characters (spaces, tabs, non-breaking spaces, etc.) with visible Unicode symbols before rendering diff output. This makes it easy to spot whitespace differences that would otherwise be indistinguishable from surrounding content.
| 3- | ░░<item>Alpha</item>
| 3+ | ░░<item>Beta</item>
The ░ characters represent U+0020 spaces used for indentation.
2. Configuration
character_visualization is a DiffConfig option accessible via
Canon::Config, environment variables, or the DiffFormatter constructor.
2.1. Values
| Value | Behaviour |
|---|---|
|
(default) The full default visualization map is applied. Spaces appear as
|
|
All visualization is disabled. Characters appear exactly as they are stored in the document. Useful when copying failure-message output into a text editor, or when downstream tooling cannot handle Unicode symbols. |
|
Reserved for future use — currently behaves identically to |
2.2. Via Canon::Config (RSpec spec_helper.rb)
Canon::Config.configure do |cfg|
# Disable for all XML diffs in this test suite
cfg.xml.diff.character_visualization = false
# Or disable for HTML as well
cfg.html.diff.character_visualization = false
end
The setting is format-specific: cfg.xml, cfg.html, cfg.json,
cfg.yaml, and cfg.string each have their own diff.character_visualization.
3. Default Visualization Map
The following characters are visualized by default:
| Name | Unicode | Visualization | Notes |
|---|---|---|---|
Space |
U+0020 |
|
Most common; appears as indentation and between tokens |
Tab |
U+0009 |
|
Structural indentation in tab-indented files |
No-Break Space |
U+00A0 |
|
Common in HTML ( |
Thin Space |
U+2009 |
|
Typographic use |
En Space |
U+2002 |
|
|
Em Space |
U+2003 |
|
|
Line Feed |
U+000A |
|
End-of-line (shown in certain modes) |
Carriage Return |
U+000D |
|
Windows line endings |
Zero-Width Space |
U+200B |
|
Invisible in output; often a bug |
The complete map is loaded from
lib/canon/diff_formatter/character_map.yml at startup.
4. Interaction with display_preprocessing
Character visualization is applied after display preprocessing.
When display_preprocessing: :pretty_print is active, Canon runs both
documents through Canon::PrettyPrinter::Xml before the line diff. The
pretty-printer produces only:
-
ASCII U+0020 spaces for indentation
-
ASCII U+000A newlines between elements
Because the default visualization map visualizes U+0020 spaces as ░,
structural indentation introduced by the pretty-printer will appear as ░
characters in context lines. This is intentional: the diff output stays
consistent regardless of how indentation was introduced.
| 1 | <root>
| 2- | ░░<item>Alpha</item>
| 2+ | ░░<item>Beta</item>
| 3 | </root>
The ░░ represents the 2-space indentation added by the pretty-printer.
If you want structure-free visualization (e.g. indentation stays as plain
spaces but content whitespace is still visualized), set
character_visualization: :content_only once that feature is implemented.
See [future-work].
5. Combining with context_lines
Context lines (unchanged lines shown around a diff hunk) are also subject to
character visualization. Reducing context_lines limits the number of
visualized lines displayed:
Canon::Config.configure do |cfg|
cfg.xml.diff.context_lines = 1 # show only 1 context line
cfg.xml.diff.character_visualization = true
end
6. Future Work: :content_only
The :content_only value is reserved for a planned DOM-level pre-serialization
pass that would:
-
Walk the parsed DOM tree before serialization.
-
Apply character visualization only to text node content.
-
Leave structural whitespace (indentation, element-separator newlines) plain.
This would allow pretty-printed output to have clean indentation while still making content whitespace visible.
The constraint that blocks this today: visualization is currently applied
as a post-serialization string substitution by the by-line formatters.
Implementing :content_only requires moving the substitution step into a
DOM walk before Nokogiri::XML::Node#to_xml is called.
When implemented, :content_only will become the recommended value for
suites that use display_preprocessing: :pretty_print.