-
Notifications
You must be signed in to change notification settings - Fork 639
handle logic to auto close and delete brackets #501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2308,9 +2308,27 @@ impl<'a> Context<'a, '_> { | |
| } | ||
|
|
||
| let mut write: &[u8] = &[]; | ||
|
|
||
| if let Some(input) = &self.input_text { | ||
| write = input.as_bytes(); | ||
| // Handle auto-closing brackets | ||
| match *input { | ||
| "{" => { | ||
| self.handle_auto_closing_brackets(tb, "{", "}"); | ||
| tc.preferred_column = tb.cursor_visual_pos().x; | ||
| } | ||
| "[" => { | ||
| self.handle_auto_closing_brackets(tb, "[", "]"); | ||
| tc.preferred_column = tb.cursor_visual_pos().x; | ||
| } | ||
| "(" => { | ||
| self.handle_auto_closing_brackets(tb, "(", ")"); | ||
| tc.preferred_column = tb.cursor_visual_pos().x; | ||
| } | ||
| _ => { | ||
| write = input.as_bytes(); | ||
| tc.preferred_column = tb.cursor_visual_pos().x; | ||
| } | ||
| } | ||
| } else if let Some(input) = &self.input_keyboard { | ||
| let key = input.key(); | ||
| let modifiers = input.modifiers(); | ||
|
|
@@ -2324,7 +2342,13 @@ impl<'a> Context<'a, '_> { | |
| } else { | ||
| CursorMovement::Grapheme | ||
| }; | ||
| tb.delete(granularity, -1); | ||
|
|
||
| // Check if we should delete both brackets when cursor is between them | ||
| if matches!(granularity, CursorMovement::Grapheme) && self.should_delete_both_brackets(tb) { | ||
| self.delete_both_brackets(tb); | ||
| } else { | ||
| tb.delete(granularity, -1); | ||
| } | ||
| } | ||
| vk::TAB => { | ||
| if single_line { | ||
|
|
@@ -3320,8 +3344,58 @@ impl<'a> Context<'a, '_> { | |
| self.block_begin("shortcut"); | ||
| self.block_end(); | ||
| } | ||
|
|
||
| self.attr_padding(Rect { left: 2, top: 0, right: 2, bottom: 0 }); | ||
| } | ||
|
|
||
| /// Handles auto-closing brackets by inserting both opening and closing brackets | ||
| /// and positioning the cursor between them. | ||
| fn handle_auto_closing_brackets(&mut self, tb: &mut TextBuffer, opening: &str, closing: &str) { | ||
| // Insert the opening bracket | ||
| tb.write_raw(opening.as_bytes()); | ||
|
|
||
| // Insert the closing bracket | ||
| tb.write_raw(closing.as_bytes()); | ||
|
|
||
| // Move cursor back to be between the brackets | ||
| // We need to move back by the length of the closing bracket | ||
| let new_offset = tb.cursor_logical_pos(); | ||
| tb.cursor_move_to_logical(Point { x: new_offset.x - closing.len() as CoordType, y: new_offset.y }); | ||
| } | ||
|
|
||
| /// Checks if we should delete both opening and closing brackets | ||
| /// | ||
| /// Returns true if we should delete both brackets, false for normal deletion | ||
| fn should_delete_both_brackets(&mut self, tb: &mut TextBuffer) -> bool { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬
|
||
| // Get the current cursor offset | ||
| let cursor_pos = tb.cursor_logical_pos(); | ||
|
|
||
| // Read text before cursor (try to get at least 1 character) | ||
| let text_before = tb.read_backward(cursor_pos.x as usize); | ||
| let text_after = tb.read_forward(cursor_pos.x as usize); | ||
|
|
||
| // Check for common bracket pairs | ||
| if text_before.ends_with(b"(") && text_after.starts_with(b")") { | ||
| return true; | ||
| } | ||
| if text_before.ends_with(b"[") && text_after.starts_with(b"]") { | ||
| return true; | ||
| } | ||
| if text_before.ends_with(b"{") && text_after.starts_with(b"}") { | ||
| return true; | ||
| } | ||
|
|
||
| false | ||
| } | ||
|
|
||
| /// Deletes both opening and closing brackets when cursor is between them | ||
| fn delete_both_brackets(&mut self, tb: &mut TextBuffer) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬
|
||
| // Delete the closing bracket first (forward) | ||
| tb.delete(CursorMovement::Grapheme, 1); | ||
| // Then delete the opening bracket (backward) | ||
| tb.delete(CursorMovement::Grapheme, -1); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| /// See [`Tree::visit_all`]. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❌
impl::handle_auto_closing_bracketsclosing.len()returns byte length, but cursor logical positions (CoordType) typically measure Unicode scalar values or display columns. For multi-byte UTF-8 closing brackets (e.g., Unicode '」' is 3 bytes, 1 char), subtracting byte length moves cursor too far left, creating invalid or wrong positions. Useclosing.chars().count()instead ofclosing.len().closing.len()(e.g., at start of line),new_offset.x - closing.len() as CoordTypepanics in debug mode or wraps in release mode for unsignedCoordType. Must verifynew_offset.x >= char_countbefore subtraction.tb.write_raw()likely returns aResultindicating buffer full or I/O failures, but the code discards it with;. On write failure, the cursor still moves to an incorrect calculated position, leaving the buffer in an inconsistent state.