Add JSON output highlighting and rainbow brackets (#79)#80
Merged
Conversation
Adds Theme helpers for JSON strings, numbers, booleans, and null, and a JsonOutputHighlighter that renders a JsonElement as Spectre.Console markup matching the indented Utf8JsonWriter layout. PrintState now applies the highlighter when JSON output is written to the terminal (file redirection and non-JSON formats are unchanged). Closes #79.
Adds Theme.GetBracketColor(depth) and Theme.FormatBracket(text, depth) using a 3-color cycle (gold1/orchid/deepskyblue1). The JSON output highlighter and the input HighlightingVisitor now color matched braces, brackets, and parentheses by their shared nesting depth, so {[(...)]} pairs are visually distinguishable. Comma and colon continue to use the existing punctuation color.
Sub-expressions inside an interpolated string interpolation are produced by a separate Lexer over the interior content, so their Start/Length positions are relative to that inner buffer, not the outer line. Recursing into them from HighlightingVisitor.Visit(InterpolatedStringExpression) caused AppendUpTo and Substring to index into the wrong buffer and smear characters from the start of the line into the rendered output (the reported case typed two interpolated strings in a row and the highlighter duplicated them). Render the whole interpolated string as a single string-literal token instead, and add a regression test that asserts the highlighter round-trips the exact input text.
Sub-expressions inside an interpolated string interpolation were previously parsed by a fresh Lexer over the cooked content with no awareness of the outer source buffer, so the resulting AST nodes carried Start/Length values relative to that sub-buffer. Downstream consumers (most visibly the syntax highlighter) either had to fall back to rendering the entire string as one literal or risk indexing into the wrong buffer. Add a position-offset constructor to Lexer that shifts every emitted token by a fixed amount, and record a per-character source-position map for every TokenType.InterpolatedString token (covering both the explicit "$"..."" form and the implicit interpolation found inside a regular double-quoted string). ExpressionParser.ParseInterpolatedStringExpression then constructs the inner sub-Lexer with the appropriate offset and uses the source map to build synthetic identifier tokens for variable references whose ranges align with the outer text. The highlighter visits these sub-expressions in place and falls back to the literal coloring for raw text chunks. Tests cover the round-trip case, expression coloring inside an interpolated expression, and a separately colored variable reference inside a quoted string.
6 tasks
Contributor
Author
Contributor
Author
|
Need to work on highlighting/colors - the light/dark theme is a valid point but it's a bit out of scope for that PR. |
There was a problem hiding this comment.
Pull request overview
Adds terminal-friendly JSON output syntax highlighting and rainbow bracket pair colorization, plus fixes to interpolated-string input highlighting by improving token position fidelity across sub-lexers.
Changes:
- Introduce
JsonOutputHighlighterand route terminal JSON output through it (without affecting redirection/CSV/table outputs). - Add bracket depth color cycling in
Themeand apply it in the input highlighter for{},[], and(). - Improve interpolated-string parsing/highlighting by tracking per-character source mappings and supporting lexing with position offsets.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| CosmosDBShell/Azure.Data.Cosmos.Shell.Parser/Lexer.cs | Adds positionOffset support and per-character source maps for interpolated string tokens. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Parser/ExpressionParser.cs | Uses interpolated-string source maps to build accurately positioned nested-expression/variable tokens. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/Theme.cs | Adds depth-cycled bracket formatting and dedicated JSON primitive formatting helpers. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.Highlighter.cs | Applies rainbow bracket coloring and fixes interpolated-string rendering to avoid duplication and enable nested highlighting. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs | Prints JSON results via the new highlighter when output is to terminal and format is JSON. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/JsonOutputHighlighter.cs | New renderer that formats JsonElement as indented Spectre.Console markup with escaping. |
| CosmosDBShell.Tests/UtilTest/JsonOutputHighlighterTests.cs | Adds unit tests for JSON markup rendering, escaping, indentation, and bracket depth cycling. |
| CosmosDBShell.Tests/Shell/HighlighterTests.cs | Adds regression/behavior tests for interpolated-string highlighting correctness and segmentation. |
- Use ReferenceEqualityComparer for the interpolated string source-map so equivalent (record-equal) Token instances cannot collide with the actual lexer-produced tokens in the lookup map. - Lex the contents of an interpolated string interpolation from the raw outer-source slice (via new Lexer.RawInput / Lexer.PositionOffset accessors) instead of the cooked token content. The cooked content collapses escape sequences (e.g. backslash-quote becomes quote) and would otherwise drift token positions inside interpolations that contain string literals with escapes such as $( "a\nb" ). The cooked-content path is retained as a fallback for synthetic tokens that have no source map. - Add a regression test that round-trips an interpolated string containing an escape inside an interpolation.
sevoku
approved these changes
May 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Implements JSON syntax highlighting for command output, plus related improvements to the input highlighter discovered while working on it.
Refs #79 (does not close it — the per-type color scheme requested by the issue's acceptance criteria lands in a follow-up PR).
What this PR does
JSON output highlighting
JsonOutputHighlighterwalks aJsonElementand renders Spectre.Console markup with the same indented layout asUtf8JsonWriter(Indented = true).Themegains dedicated helpers for each JSON value kind (FormatJsonString,FormatJsonNumber,FormatJsonBoolean,FormatJsonNull) so the color scheme can evolve per type without touching call sites.ShellInterpreter.PrintStateroutes JSON results to the highlighter when the output goes to the terminal; file redirection and CSV/Table formats are unchanged, so MCP and existing scripts are unaffected.violet. A follow-up PR will introduce the distinct color scheme requested by the issue (kept separate to keep this one focused).Bracket pair colorization
Theme.GetBracketColor(int depth)/Theme.FormatBracket(text, depth)cycle throughgold1/orchid/deepskyblue1.HighlightingVisitorshare a depth counter across{},[], and(), so nested structures andParensExpressioncontents pick up the rainbow coloring familiar from modern editors.Input highlighter fixes
Visit(InterpolatedStringExpression)no longer smears characters from the start of the line into the rendered output when an interpolated string contains$(...)or$VAR— fixes a duplication bug where typing two interpolated strings back to back rendered an extra copy.Lexernow accepts apositionOffset, and interpolated string tokens carry a per-character source map soExpressionParser.ParseInterpolatedStringExpressioncan build sub-lexers and synthetic identifier tokens whose positions are accurate against the outer source buffer. Operators, numbers, parens, and variable references inside$(...)and$namereferences in"..."are now colored as the corresponding expression elements.$(...)interiors are lexed from the raw outer-source slice (via the newLexer.RawInput/PositionOffsetaccessors) instead of the cooked token content, so escape sequences inside the interpolation no longer drift token positions.Tests
JsonOutputHighlighterTests(5 tests): primitive type colors, indentation, empty containers, JSON-and-markup escaping, depth-cycled bracket coloring.TestInterpolatedExpressionDoesNotDuplicateText,TestInterpolatedExpressionContentsAreColoredAsExpression,TestInterpolatedVariableIsColoredSeparately,TestInterpolatedExpressionWithEscapesRoundTrips.Out of scope (separate PR)