From daa126cc07f565c2d90b4a0430a205c3bc081c60 Mon Sep 17 00:00:00 2001 From: vasilito Date: Mon, 6 Jul 2026 21:31:23 +0300 Subject: [PATCH] W30: Closing bracket pair highlight (also check before cursor) The existing bracket_flash only triggered when the cursor was ON a bracket character. This meant that after typing a closing bracket (the most common trigger scenario), the cursor sits AFTER the bracket and the flash doesn't fire. Update update_bracket_flash to also check the character BEFORE the cursor (pos - 1) and use it as a fallback. The pair position is adjusted accordingly (the match is at pos - 1 for the before- cursor case). Tests: 1393 pass, zero warnings. --- .../recipes/tui/tlc/source/src/editor/mod.rs | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/local/recipes/tui/tlc/source/src/editor/mod.rs b/local/recipes/tui/tlc/source/src/editor/mod.rs index 4df082fc20..80990d538d 100644 --- a/local/recipes/tui/tlc/source/src/editor/mod.rs +++ b/local/recipes/tui/tlc/source/src/editor/mod.rs @@ -824,24 +824,47 @@ impl Editor { fn update_bracket_flash(&mut self) { let pos = self.cursor.position(); let text = self.buffer.as_string(); - if pos >= text.len() || !text.is_char_boundary(pos) { + if pos > text.len() || !text.is_char_boundary(pos) { self.bracket_flash = None; return; } - let ch = text[pos..].chars().next().unwrap(); - let open = "([{<"; - let close = ")]}>"; - let result = if let Some(idx) = open.find(ch) { - let target = close.as_bytes()[idx] as char; - Self::find_matching_forward(&text, pos, ch, target) - .map(|end| (pos, end)) - } else if let Some(idx) = close.find(ch) { - let target = open.as_bytes()[idx] as char; - Self::find_matching_backward(&text, pos, ch, target) - .map(|start| (start, pos)) + // Check the character UNDER the cursor first. If the cursor + // sits on an opening bracket, the match is forward. If it + // sits on a closing bracket, the match is backward. + let ch_at = text[pos..].chars().next(); + // Also check the character BEFORE the cursor (pos - 1). + // When the user types a closing bracket, the cursor sits + // *after* the bracket; we still want to flash the pair. + let ch_before = if pos > 0 && text.is_char_boundary(pos - 1) { + text[..pos].chars().next_back() } else { None }; + let open = "([{<"; + let close = ")]}>"; + let resolve = |c: char| -> Option<(usize, usize)> { + if let Some(idx) = open.find(c) { + let target = close.as_bytes()[idx] as char; + Self::find_matching_forward(&text, pos, c, target) + .map(|end| (pos, end)) + } else if let Some(idx) = close.find(c) { + let target = open.as_bytes()[idx] as char; + Self::find_matching_backward(&text, pos, c, target) + .map(|start| (start, pos)) + } else { + None + } + }; + // Prefer the char under the cursor. Fall back to the char + // before (for the typed-closing-bracket case). + let result = ch_at.and_then(&resolve).or_else(|| { + ch_before.and_then(&resolve).map(|(s, _)| { + // For backward lookup the match is at (pos - 1) and + // the opening bracket is before it. + let _ = pos; + (s, pos.saturating_sub(1)) + }) + }); self.bracket_flash = result; }