Skip to content

Keep code literal and match its background to the theme - #2

Open
scottjones wants to merge 4 commits into
omacom:masterfrom
scottjones:literal-code-spans
Open

scottjones wants to merge 4 commits into
omacom:masterfrom
scottjones:literal-code-spans

Conversation

@scottjones

@scottjones scottjones commented Jul 29, 2026

Copy link
Copy Markdown

Code containing Markdown characters was being styled as prose: default_line_height lost its underscores to italics, *value* inside a fenced block became emphasis, and the caret skipped visible characters as though they were hidden markup.

Treat inline code and triple-backtick fenced blocks as literal text, using the same parsing for highlighting and caret movement. Preserve underscores inside identifiers in ordinary prose. Emphasis and links can still surround inline code, while the code keeps its own styling and background. Escaped opening backticks stay literal, and closing fence lines no longer report hidden markers.

Code backgrounds now use the Omarchy theme's lighter_background. If that value is missing, invalid, or identical to the page background, Backend mixes the page color 6% toward the text color. This also works on pure black and white pages, and reloading a theme cannot retain the previous theme's panel color.

Validation

  • Rebased onto current master (8f98892).
  • QT_QPA_PLATFORMTHEME=generic bin/test: 21 passed, 0 failed on Qt 6.11.2.
  • bin/build builds the application.
  • Regression coverage includes escaped backticks, code inside bold/italic/link text, caret ranges on opening/interior/closing fence lines, unterminated fences and fence deletion, and theme color selection and fallback across reloads.

The inline-code portion overlaps with #48 and includes the same code-last rendering order needed to preserve nested code styling. This PR also handles underscores in prose, fenced blocks, and theme-derived code backgrounds.

The existing parser remains limited to single-backtick inline spans and the editor's triple-backtick fence convention. Fence backgrounds are painted per character, so their right edges follow line lengths and empty lines have no panel.

@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed both halves against the source and on a disposable VM. bin/test builds clean and all 16 tests pass on Qt 6.11.1. Nothing pushed — everything below is minor, and the two behaviour questions are yours to call.

The theme half holds up under the checks I had doubts about. m_themeLighterBackground.clear() at backend.cpp:599 is unconditional with no early return above it, and loadOmarchyTheme() is the only writer, so switching themes at runtime cannot inherit the previous theme's panel. .name() is never taken on an invalid QColorlighter.isValid() guards the theme string and blend() always returns a valid fromRgbF colour — and that guard is load-bearing, since QColor("garbage").name() is #000000, which would have put a black panel on a light theme. The 6% mix measures #0f0f0f (lightness 15) on a pure black page and #f0f0f0 (240) on a pure white one, so the comment's claim about mixing beating lightening is right. All 22 stock themes ship lighter_background, and the only two where it equals the page — last-horizon and solitude — are exactly the fallback case; they land on #1a191a and #1b1e20.

Three small things:

  • markdownhighlighter.cpp:118 stores the closing fence line as Prose — opening and interior lines get InsideFence, the closing one does not. That is what your own test asserts and it matches the fence-parity convention smartReturn() already uses in Main.qml, so it is defensible. The consequence is that backend.cpp:387 does not take its early return on that line: hiddenRangesAt() parses it as prose while highlightBlock() has painted the whole line with m_fenceFormat and hidden nothing. For a bare ``` no ranges come back and it is harmless, but on a line like ``` **x** the caret skips over four characters the user can see. Cheapest fix is to widen the guard to cover a fence line as well as an interior one.
  • markdownhighlighter.cpp:225 — codeSpans() does not honour a backslash-escaped backtick, and it now decides whether emphasis is dropped rather than only where a panel is drawn. use \ in code like `x`renderscode` italic on master and renders it literally here. CommonMark agrees with your version for that particular line, so this is arguably a fix, but escaped backticks are a real divergence.
  • The comment at markdownhighlighter.cpp:245 says markup that merely wraps a code span still applies, and the markup does — but the code span loses its panel while doing so. setFormat() replaces rather than merges, so the bold format applied over the content wipes the background: for **\file_name` only**` the whole run comes back with no background and visible backticks. This is not something you introduced — master does exactly the same — but the comment reads like a promise the rendering does not quite keep.

Unterminated fences behave correctly: every remaining block stays InsideFence to end of document, which is what CommonMark specifies, and the initial previousBlockState() == -1 is handled. Qt's incremental rehighlighting cascades properly when a fence is typed, deleted or split.

Heads up that PRs #5, #8, #15 and #17 all touch src/markdownhighlighter.{h,cpp} and tests/tst_omawrite.cpp; #15 adds another inline kind and will want the same code-span exclusion this PR introduces.

frivas pushed a commit to frivas/omawrite that referenced this pull request Aug 31, 2026
Resolved against PR omacom#34, which restructured loadOmarchyTheme() to parse into
locals before applying: the new lighter_background key follows that shape, and
the per-load reset moves down with it.
frivas added a commit to frivas/omawrite that referenced this pull request Aug 31, 2026
Resolved against PR omacom#34, which restructured loadOmarchyTheme() to parse into
locals before applying: the new lighter_background key follows that shape, and
the per-load reset moves down with it.
scottjones and others added 4 commits September 17, 2026 00:06
An identifier like `default_line_height` came out italic with its
underscores hidden: the inline rules ran over the raw line, so every
underscore between backticks looked like an emphasis marker. Collect the
code spans first and drop any markup whose markers land inside one.
Matching on the markers rather than the whole span keeps emphasis that
merely wraps code, as in **bold with `code` inside**, intact.

Underscores now also need a word boundary to open emphasis, the way
CommonMark and GitHub read them, so snake_case_name is left alone in
prose too. Asterisks still delimit anywhere, and Ctrl+B and Ctrl+I
insert asterisks, so nothing changes for the toolbar.

Both fixes live in inlineMarkup, the source the editor shares through
Backend::hiddenRangesAt, so the caret stops skipping over underscores it
should never have hidden.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything between a pair of ``` fences is literal, but the highlighter
had no memory of where it stood, so a code block was read as prose:
snake_case_name came out italic with its underscores hidden, and a
comment opening a line was set as a bold heading. The editor already
counted fences for smartReturn, so it knew what the highlighter did not.

Carry the state from block to block instead. Inside a fence nothing is
markup and no markers are hidden; the code sits on the same panel inline
code gets, and the fences themselves recede the way a heading's `#` does.
The state lives on the block, which is how Backend::hiddenRangesAt learns
to stop skipping the caret over underscores in code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Inline code and fenced blocks sat on a hardcoded #1c1a1a, a warm grey
left over from before the editor followed the desktop. On the current
theme it read as a smudge against a green page while every colour around
it tracked the theme.

Omarchy themes name a lighter background for panels exactly like this
one, so use the shade the theme chose. Not every theme sets the key, and
a few set it to the page background, which would leave code with no
panel at all; those fall back to a shade mixed from the page towards the
text. Mixing rather than lightening keeps the shade moving on a pure
black background, and lands on the theme's own hue either way.

Backend resolves the colour and publishes it, the way it already does
for the page and the accent, so the highlighter is handed a colour to
paint rather than a theme to interpret.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Handle closing fence lines in caret ranges and escaped opening backticks in inline parsing. Apply code styling after enclosing markup, as in PR omacom#48, so code retains its panel and literal appearance.

Make Backend the sole source of code-panel colors, remove unused QML exposure, and clarify fence transitions. Cover the review cases with rendering, caret, escape, and theme reload regressions.
@scottjones scottjones changed the title Read code as literal text, and put its panel on the theme Keep code literal and match its background to the theme Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants