From 9b36e3d0091cad38b9b7bfcae21e0d17ddd95586 Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Fri, 1 Jul 2022 15:06:16 -0700 Subject: [PATCH] Highlight matching bracket when newest selection head is on a bracket --- assets/keymaps/vim.json | 1 + crates/editor/src/editor.rs | 3 ++ .../editor/src/highlight_matching_bracket.rs | 40 +++++++++++++++++++ crates/vim/src/motion.rs | 23 ++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 crates/editor/src/highlight_matching_bracket.rs diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json index 0d2a611d46..e8bf95061c 100644 --- a/assets/keymaps/vim.json +++ b/assets/keymaps/vim.json @@ -37,6 +37,7 @@ "ignorePunctuation": true } ], + "shift-%": "vim::Matching", "escape": "editor::Cancel" } }, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 9243df0004..3a17e53559 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1,5 +1,6 @@ pub mod display_map; mod element; +mod highlight_matching_bracket; mod hover_popover; pub mod items; mod link_go_to_definition; @@ -31,6 +32,7 @@ use gpui::{ ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; +use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; pub use language::{char_kind, CharKind}; use language::{ @@ -1422,6 +1424,7 @@ impl Editor { } self.refresh_code_actions(cx); self.refresh_document_highlights(cx); + refresh_matching_bracket_highlights(self, cx); } self.pause_cursor_blinking(cx); diff --git a/crates/editor/src/highlight_matching_bracket.rs b/crates/editor/src/highlight_matching_bracket.rs new file mode 100644 index 0000000000..3edeb80003 --- /dev/null +++ b/crates/editor/src/highlight_matching_bracket.rs @@ -0,0 +1,40 @@ +use gpui::ViewContext; + +use crate::Editor; + +enum MatchingBracketHighlight {} + +pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewContext) { + editor.clear_background_highlights::(cx); + + let newest_selection = editor.selections.newest::(cx); + let snapshot = editor.snapshot(cx); + if let Some((opening_range, closing_range)) = snapshot + .buffer_snapshot + .enclosing_bracket_ranges(newest_selection.range()) + { + let head = newest_selection.head(); + let range_to_highlight = if opening_range.contains(&head) { + Some(closing_range) + } else if closing_range.contains(&head) { + Some(opening_range) + } else { + None + }; + + if let Some(range_to_highlight) = range_to_highlight { + let anchor_range = snapshot + .buffer_snapshot + .anchor_before(range_to_highlight.start) + ..snapshot + .buffer_snapshot + .anchor_after(range_to_highlight.end); + + editor.highlight_background::( + vec![anchor_range], + |theme| theme.editor.document_highlight_read_background, + cx, + ) + } + } +} diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 30c6c78c05..d9e4bf084c 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -30,6 +30,7 @@ pub enum Motion { EndOfLine, StartOfDocument, EndOfDocument, + Matching, } #[derive(Clone, Deserialize, PartialEq)] @@ -65,7 +66,8 @@ actions!( EndOfLine, CurrentLine, StartOfDocument, - EndOfDocument + EndOfDocument, + Matching, ] ); impl_actions!(vim, [NextWordStart, NextWordEnd, PreviousWordStart]); @@ -85,6 +87,7 @@ pub fn init(cx: &mut MutableAppContext) { motion(Motion::StartOfDocument, cx) }); cx.add_action(|_: &mut Workspace, _: &EndOfDocument, cx: _| motion(Motion::EndOfDocument, cx)); + cx.add_action(|_: &mut Workspace, _: &Matching, cx: _| motion(Motion::Matching, cx)); cx.add_action( |_: &mut Workspace, &NextWordStart { ignore_punctuation }: &NextWordStart, cx: _| { @@ -136,7 +139,7 @@ impl Motion { } match self { - EndOfLine | NextWordEnd { .. } => true, + EndOfLine | NextWordEnd { .. } | Matching => true, Left | Right | StartOfLine | NextWordStart { .. } | PreviousWordStart { .. } => false, _ => panic!("Exclusivity not defined for {self:?}"), } @@ -172,6 +175,7 @@ impl Motion { CurrentLine => (end_of_line(map, point), SelectionGoal::None), StartOfDocument => (start_of_document(map, point), SelectionGoal::None), EndOfDocument => (end_of_document(map, point), SelectionGoal::None), + Matching => (matching(map, point), SelectionGoal::None), } } @@ -341,3 +345,18 @@ fn end_of_document(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { *new_point.column_mut() = point.column(); map.clip_point(new_point, Bias::Left) } + +fn matching(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint { + let offset = point.to_offset(map, Bias::Left); + if let Some((open_range, close_range)) = + map.buffer_snapshot.enclosing_bracket_ranges(offset..offset) + { + if open_range.contains(&offset) { + close_range.start.to_display_point(map) + } else { + open_range.start.to_display_point(map) + } + } else { + point + } +}