Fix rendering issues around inline and fenced code in Markdown text fields.
Review Request #2473 — Created May 1, 2022 and updated
We try to keep a consistent presentation between Markdown text editing and rendering in text fields, as opposed to the more standard approach of having a "Preview" tab that's separate from the editor. In Review Board 4, we tweaked the styling to try to more clearly highlight any inline code literals by showing a light-grey background and a rounded box around it. This is a pretty common appearance in many apps, including Slack and Discord. To get this to render correctly, we had to put some tricks in like negative margins to compensate for the borders and keep everything aligned. The prior approach had a few issues: 1. During editing, there was a faint border between the highlighting for the code literal backticks and the content (and, if you zoomed in, you'd see rounding on the border) 2. If putting in a fenced code block without a language mapped to a CodeMirror mode, the content would appear as one big inline literal while editing. 3. This inline literal, due to our styling and CodeMirror behavior, would display the cursor with a height of the full code block. Part of the difficulty is that CodeMirror doesn't tell you when a code literal starts or ends, and may break it up into multiple pieces (opening backtick, one or more spans of content, closing backtick). It also reuses the same styles for plain text in code blocks. This change switches up the approach. To eliminate the negative margins, but to keep the borders where we want them, we no longer apply the border styling on the inline literal selectors themselves. We instead set up `:before` styles, absolutely-positioned relative to the owning span, offset out by `-1px` on each side. This is the equivalent of what we had, but doesn't impact layout. We apply the top/bottom borders to the inline literals, and then apply a left border (and radiuses) to the classes representing the opening/closing backticks. Another rule then carefully matches the right-most backtick class (using a combination of `:not` and `+` to allow text to break up the adjacent selectors) and undoes the left border and adds a right border. The result of that is what appears to be one consistent span of text styled as one unbroken code literal. The second problem to solve involves the code blocks. CodeMirror does not allow us to provide custom CSS classes for code blocks, but it does allow us to create our own plain text lexer/highlighter mode, which itself sets a suitable CSS class. We then map `text/plain` to that lexer, so it's used as the fallback. This ensures that any fenced code blocks either without a specific language or with one that's not supported will edit and render correctly. Testing Done: Tested in Firefox and in Chrome, zooming far in (around 500%) and checking the contents of code literals and code blocks in various circumstances. This includes code blocks which are flush with the left/right edges (in the review request fields, comment dialog, and draft banners) and ones that are not (Review Dialog). Reviewed at https://reviews.reviewboard.org/r/12225/