WIP

Max Brunsfeld created

Change summary

Cargo.lock                          |  1 
crates/find/Cargo.toml              |  1 
crates/find/src/find.rs             | 88 ++++++++++++++++++++++++++++--
crates/theme/src/theme.rs           |  6 ++
crates/zed/assets/themes/_base.toml | 28 +++++++++
5 files changed, 118 insertions(+), 6 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1727,6 +1727,7 @@ dependencies = [
  "editor",
  "gpui",
  "postage",
+ "theme",
  "workspace",
 ]
 

crates/find/Cargo.toml 🔗

@@ -10,5 +10,6 @@ path = "src/find.rs"
 aho-corasick = "0.7"
 editor = { path = "../editor" }
 gpui = { path = "../gpui" }
+theme = { path = "../theme" }
 workspace = { path = "../workspace" }
 postage = { version = "0.4.1", features = ["futures-traits"] }

crates/find/src/find.rs 🔗

@@ -1,4 +1,4 @@
-use aho_corasick::AhoCorasick;
+use aho_corasick::AhoCorasickBuilder;
 use editor::{Editor, EditorSettings};
 use gpui::{
     action, elements::*, keymap::Binding, Entity, MutableAppContext, RenderContext, View,
@@ -10,6 +10,14 @@ use workspace::{ItemViewHandle, Settings, Toolbar, Workspace};
 
 action!(Deploy);
 action!(Cancel);
+action!(ToggleMode, SearchMode);
+
+#[derive(Clone, Copy)]
+pub enum SearchMode {
+    WholeWord,
+    CaseSensitive,
+    Regex,
+}
 
 pub fn init(cx: &mut MutableAppContext) {
     cx.add_bindings([
@@ -18,12 +26,16 @@ pub fn init(cx: &mut MutableAppContext) {
     ]);
     cx.add_action(FindBar::deploy);
     cx.add_action(FindBar::cancel);
+    cx.add_action(FindBar::toggle_mode);
 }
 
 struct FindBar {
     settings: watch::Receiver<Settings>,
     query_editor: ViewHandle<Editor>,
     active_editor: Option<ViewHandle<Editor>>,
+    case_sensitive_mode: bool,
+    whole_word_mode: bool,
+    regex_mode: bool,
 }
 
 impl Entity for FindBar {
@@ -39,10 +51,25 @@ impl View for FindBar {
         cx.focus(&self.query_editor);
     }
 
-    fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
-        ChildView::new(&self.query_editor)
+    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
+        let theme = &self.settings.borrow().theme.find;
+        Flex::column()
+            .with_child(
+                ChildView::new(&self.query_editor)
+                    .contained()
+                    .with_style(theme.query.container)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::column()
+                    .with_child(self.render_mode_button("Aa", SearchMode::CaseSensitive, theme, cx))
+                    .with_child(self.render_mode_button("|ab|", SearchMode::WholeWord, theme, cx))
+                    .with_child(self.render_mode_button(".*", SearchMode::Regex, theme, cx))
+                    .contained()
+                    .with_style(theme.mode_button_group)
+                    .boxed(),
+            )
             .contained()
-            .with_style(self.settings.borrow().theme.selector.input_editor.container)
             .boxed()
     }
 }
@@ -67,7 +94,7 @@ impl FindBar {
                     Arc::new(move |_| {
                         let settings = settings.borrow();
                         EditorSettings {
-                            style: settings.theme.selector.input_editor.as_editor(),
+                            style: settings.theme.find.query.as_editor(),
                             tab_size: settings.tab_size,
                             soft_wrap: editor::SoftWrap::None,
                         }
@@ -82,10 +109,37 @@ impl FindBar {
         Self {
             query_editor,
             active_editor: None,
+            case_sensitive_mode: false,
+            whole_word_mode: false,
+            regex_mode: false,
             settings,
         }
     }
 
+    fn render_mode_button(
+        &self,
+        icon: &str,
+        mode: SearchMode,
+        theme: &theme::Find,
+        cx: &mut RenderContext<Self>,
+    ) -> ElementBox {
+        let is_active = self.is_mode_enabled(mode);
+        MouseEventHandler::new::<Self, _, _, _>(0, cx, |state, _| {
+            let style = match (is_active, state.hovered) {
+                (false, false) => &theme.mode_button,
+                (false, true) => &theme.hovered_mode_button,
+                (true, false) => &theme.active_mode_button,
+                (true, true) => &theme.active_hovered_mode_button,
+            };
+            Label::new(icon.to_string(), style.text.clone())
+                .contained()
+                .with_style(style.container)
+                .boxed()
+        })
+        .on_click(move |cx| cx.dispatch_action(ToggleMode(mode)))
+        .boxed()
+    }
+
     fn deploy(workspace: &mut Workspace, _: &Deploy, cx: &mut ViewContext<Workspace>) {
         let settings = workspace.settings();
         workspace.active_pane().update(cx, |pane, cx| {
@@ -102,6 +156,25 @@ impl FindBar {
             .update(cx, |pane, cx| pane.hide_toolbar(cx));
     }
 
+    fn is_mode_enabled(&self, mode: SearchMode) -> bool {
+        match mode {
+            SearchMode::WholeWord => self.whole_word_mode,
+            SearchMode::CaseSensitive => self.case_sensitive_mode,
+            SearchMode::Regex => self.regex_mode,
+        }
+    }
+
+    fn toggle_mode(&mut self, ToggleMode(mode): &ToggleMode, cx: &mut ViewContext<Self>) {
+        eprintln!("TOGGLE MODE");
+        let value = match mode {
+            SearchMode::WholeWord => &mut self.whole_word_mode,
+            SearchMode::CaseSensitive => &mut self.case_sensitive_mode,
+            SearchMode::Regex => &mut self.regex_mode,
+        };
+        *value = !*value;
+        cx.notify();
+    }
+
     fn on_query_editor_event(
         &mut self,
         _: ViewHandle<Editor>,
@@ -117,7 +190,10 @@ impl FindBar {
                     return;
                 }
 
-                let search = AhoCorasick::new_auto_configured(&[search]);
+                let search = AhoCorasickBuilder::new()
+                    .auto_configure(&[&search])
+                    .ascii_case_insensitive(!self.case_sensitive_mode)
+                    .build(&[&search]);
                 let buffer = editor.buffer().read(cx).snapshot(cx);
                 let ranges = search
                     .stream_find_iter(buffer.bytes_in_range(0..buffer.len()))

crates/theme/src/theme.rs 🔗

@@ -90,6 +90,12 @@ pub struct Tab {
 
 #[derive(Clone, Deserialize, Default)]
 pub struct Find {
+    pub query: InputEditorStyle,
+    pub mode_button_group: ContainerStyle,
+    pub mode_button: ContainedText,
+    pub active_mode_button: ContainedText,
+    pub hovered_mode_button: ContainedText,
+    pub active_hovered_mode_button: ContainedText,
     pub match_background: Color,
 }
 

crates/zed/assets/themes/_base.toml 🔗

@@ -321,3 +321,31 @@ tab_summary_spacing = 10
 
 [find]
 match_background = "$state.highlighted_line"
+
+[find.mode_button]
+extends = "$text.1"
+
+[find.mode_button_group]
+corner_radius = 6
+border = { width = 1, color = "$border.0" }
+
+[find.active_mode_button]
+extends = "$find.mode_button"
+background = "$surface.2"
+
+[find.hovered_mode_button]
+extends = "$find.mode_button"
+background = "$surface.2"
+
+[find.active_hovered_mode_button]
+extends = "$find.mode_button"
+background = "$surface.2"
+
+[find.query]
+background = "$surface.1"
+corner_radius = 6
+padding = { left = 16, right = 16, top = 7, bottom = 7 }
+text = "$text.0"
+placeholder_text = "$text.2"
+selection = "$selection.host"
+border = { width = 1, color = "$border.0" }