mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 17:09:13 -10:00
Merge pull request #7171 from Lstarsky0/fix/parse-markup-inside-emphasis
fix(tui): render code and links inside emphasis
This commit is contained in:
5 files changed
+15
-19
No files matched your search
+1
-1
@@ -124,7 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Notes stop swallowing a backslash:** a transcript note is machine-generated and names paths, but it went through the markdown renderer, where CommonMark §2.4 reads a backslash before ASCII punctuation as an escape. A note naming `C:\Users\asus\.codewhale\setup_state.json` therefore printed as `C:\Users\asus.codewhale\setup_state.json` — a directory that does not exist, in the one line a user would copy to find the real one. Notes are now escaped where they are rendered. Fenced blocks, and lines carrying inline code, emphasis or a table row, are passed through untouched, because the renderer already copies those verbatim and doubling a backslash inside one would print it doubled.
|
||||
- **Notes stop swallowing a backslash:** a transcript note is machine-generated and names paths, but it went through the markdown renderer, where CommonMark §2.4 reads a backslash before ASCII punctuation as an escape. A note naming `C:\Users\asus\.codewhale\setup_state.json` therefore printed as `C:\Users\asus.codewhale\setup_state.json` — a directory that does not exist, in the one line a user would copy to find the real one. Notes are now escaped where they are rendered. Fenced blocks, and lines carrying inline code or a table row, are passed through untouched, because the renderer already copies those verbatim and doubling a backslash inside one would print it doubled.
|
||||
- **Setup state survives a second instance:** two running Codewhale windows share one home, and both rewrite `setup_state.json` when the provider or model changes. The replacing rename is now retried briefly when the destination is held open for a read — Windows refuses it outright while that handle lives — instead of surfacing a write the caller could have made. The failure message also carries the underlying I/O error instead of only the outer context, so the next occurrence is diagnosable.
|
||||
- **`doctor` stops flagging a user's own temp-named logs:** the `stale_setup_temp_files` check matched any file whose name starts with `.tmp`, so scratch files such as `.tmp-build-release.log` made a clean home report as half-applied. It now matches the shape a staged write leaves when it never lands (`.tmpXXXXXX`, no extension).
|
||||
|
||||
|
||||
@@ -1777,7 +1777,7 @@ fn parse_inline_spans(line: &str, base_style: Style, link_style: Style) -> Vec<I
|
||||
if let Some(end) = rest.strip_prefix("**").and_then(|s| s.find("**")) {
|
||||
let inner = &rest[2..2 + end];
|
||||
if !inner.starts_with(char::is_whitespace) && !inner.ends_with(char::is_whitespace) {
|
||||
out.push(InlineToken::new(inner.to_string(), bold_style, None));
|
||||
out.extend(parse_inline_spans(inner, bold_style, link_style));
|
||||
rest = &rest[2 + end + 2..];
|
||||
continue;
|
||||
}
|
||||
@@ -1785,7 +1785,7 @@ fn parse_inline_spans(line: &str, base_style: Style, link_style: Style) -> Vec<I
|
||||
// __bold__
|
||||
if let Some(end) = rest.strip_prefix("__").and_then(|s| s.find("__")) {
|
||||
let inner = &rest[2..2 + end];
|
||||
out.push(InlineToken::new(inner.to_string(), bold_style, None));
|
||||
out.extend(parse_inline_spans(inner, bold_style, link_style));
|
||||
rest = &rest[2 + end + 2..];
|
||||
continue;
|
||||
}
|
||||
@@ -1803,7 +1803,7 @@ fn parse_inline_spans(line: &str, base_style: Style, link_style: Style) -> Vec<I
|
||||
&& !inner.ends_with(char::is_whitespace)
|
||||
&& !after.starts_with(|c: char| c.is_alphanumeric() || c == '_')
|
||||
{
|
||||
out.push(InlineToken::new(inner.to_string(), italic_style, None));
|
||||
out.extend(parse_inline_spans(inner, italic_style, link_style));
|
||||
rest = after;
|
||||
continue;
|
||||
}
|
||||
@@ -1828,7 +1828,7 @@ fn parse_inline_spans(line: &str, base_style: Style, link_style: Style) -> Vec<I
|
||||
// Closing delimiter must not be immediately followed by a
|
||||
// letter, digit, or underscore.
|
||||
if openable && !after.starts_with(|c: char| c.is_alphanumeric() || c == '_') {
|
||||
out.push(InlineToken::new(inner.to_string(), italic_style, None));
|
||||
out.extend(parse_inline_spans(inner, italic_style, link_style));
|
||||
rest = after;
|
||||
continue;
|
||||
}
|
||||
@@ -1843,7 +1843,7 @@ fn parse_inline_spans(line: &str, base_style: Style, link_style: Style) -> Vec<I
|
||||
// ~~strikethrough~~
|
||||
if let Some(end) = rest.strip_prefix("~~").and_then(|s| s.find("~~")) {
|
||||
let inner = &rest[2..2 + end];
|
||||
out.push(InlineToken::new(inner.to_string(), strike_style, None));
|
||||
out.extend(parse_inline_spans(inner, strike_style, link_style));
|
||||
rest = &rest[2 + end + 2..];
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Notes stop swallowing a backslash:** a transcript note is machine-generated and names paths, but it went through the markdown renderer, where CommonMark §2.4 reads a backslash before ASCII punctuation as an escape. A note naming `C:\Users\asus\.codewhale\setup_state.json` therefore printed as `C:\Users\asus.codewhale\setup_state.json` — a directory that does not exist, in the one line a user would copy to find the real one. Notes are now escaped where they are rendered. Fenced blocks, and lines carrying inline code, emphasis or a table row, are passed through untouched, because the renderer already copies those verbatim and doubling a backslash inside one would print it doubled.
|
||||
- **Notes stop swallowing a backslash:** a transcript note is machine-generated and names paths, but it went through the markdown renderer, where CommonMark §2.4 reads a backslash before ASCII punctuation as an escape. A note naming `C:\Users\asus\.codewhale\setup_state.json` therefore printed as `C:\Users\asus.codewhale\setup_state.json` — a directory that does not exist, in the one line a user would copy to find the real one. Notes are now escaped where they are rendered. Fenced blocks, and lines carrying inline code or a table row, are passed through untouched, because the renderer already copies those verbatim and doubling a backslash inside one would print it doubled.
|
||||
- **Setup state survives a second instance:** two running Codewhale windows share one home, and both rewrite `setup_state.json` when the provider or model changes. The replacing rename is now retried briefly when the destination is held open for a read — Windows refuses it outright while that handle lives — instead of surfacing a write the caller could have made. The failure message also carries the underlying I/O error instead of only the outer context, so the next occurrence is diagnosable.
|
||||
- **`doctor` stops flagging a user's own temp-named logs:** the `stale_setup_temp_files` check matched any file whose name starts with `.tmp`, so scratch files such as `.tmp-build-release.log` made a clean home report as half-applied. It now matches the shape a staged write leaves when it never lands (`.tmpXXXXXX`, no extension).
|
||||
|
||||
|
||||
@@ -365,9 +365,8 @@ pub(super) fn system_body_style() -> Style {
|
||||
/// backslash:
|
||||
///
|
||||
/// - a fenced block, closed only by a run at least as long as its opener;
|
||||
/// - a line carrying an inline delimiter the renderer treats as a passthrough
|
||||
/// (`` ` ``, `**`, `~~`) — inline code, bold and strikethrough are the shapes
|
||||
/// a note's third-party text (MCP guidance, extension prompts) brings with it;
|
||||
/// - a line carrying inline code (`` ` ``), a shape a note's third-party text
|
||||
/// (MCP guidance, extension prompts) brings with it;
|
||||
/// - a table row.
|
||||
///
|
||||
/// A line that is both prose and delimited keeps today's behavior rather than
|
||||
@@ -419,9 +418,5 @@ fn carries_verbatim_inline_markdown(line: &str) -> bool {
|
||||
if trimmed.starts_with('|') && trimmed.ends_with('|') && trimmed.len() > 1 {
|
||||
return true;
|
||||
}
|
||||
// `__bold__` and `_italic_` are deliberately absent: `_` is everywhere in
|
||||
// identifiers (`setup_state.json`), and a false positive there would leave
|
||||
// the paths this pass exists to protect untouched. `*` covers both the bold
|
||||
// and italic forms and is rare enough in a machine note to keep this cheap.
|
||||
line.contains('`') || line.contains('*') || line.contains("~~")
|
||||
line.contains('`')
|
||||
}
|
||||
@@ -177,10 +177,11 @@ fn a_system_note_leaves_a_fenced_block_verbatim() {
|
||||
|
||||
#[test]
|
||||
fn a_system_note_leaves_inline_markup_and_table_rows_verbatim() {
|
||||
// The renderer copies inline code, bold, strikethrough and table cells
|
||||
// verbatim, so doubling a backslash inside one prints it doubled. These are
|
||||
// the shapes third-party guidance brings into a note (MCP servers,
|
||||
// extension prompts), and they already render a Windows path correctly.
|
||||
// The renderer copies inline code and table cells verbatim, so doubling a
|
||||
// backslash inside one prints it doubled; bold and strikethrough read a
|
||||
// doubled backslash back as one. These are the shapes third-party guidance
|
||||
// brings into a note (MCP servers, extension prompts), and each renders a
|
||||
// Windows path correctly.
|
||||
for content in [
|
||||
r"see `C:\Users\asus\.codewhale` for the file".to_string(),
|
||||
r"**C:\Users\asus** is the home".to_string(),
|
||||
|
||||
Reference in new issue
Block a user