Browse Source

Feat search (#32)

* feat search

* 修改default.yaml

* 修改usize溢出bug,修改exec_search前后部分按键不能使用

---------

Co-authored-by: zhuweihao12138 <746026882@qq.com>
Co-authored-by: GnoCiYeH <heyicong@dragonos.org>
Weihao Zhu 5 months ago
parent
commit
c14ec5e894

+ 5 - 0
src/application/handler/app.rs

@@ -22,6 +22,11 @@ pub fn to_workspace_mode(app: &mut Application) -> Result<()> {
     Ok(())
 }
 
+pub fn to_search_mode(app: &mut Application) -> Result<()> {
+    app.switch_mode(ModeKey::Search);
+    OK(())
+}
+
 pub fn to_delete_mode(app: &mut Application) -> Result<()> {
     app.switch_mode(ModeKey::Delete);
     Ok(())

+ 1 - 0
src/application/handler/mod.rs

@@ -8,6 +8,7 @@ mod cursor;
 mod delete;
 mod insert;
 mod monitor;
+mod search;
 mod normal;
 mod workspace;
 

+ 84 - 0
src/application/handler/search.rs

@@ -0,0 +1,84 @@
+use crate::application::mode::ModeData;
+use crate::application::Application;
+use crate::errors::*;
+use crate::util::{position::Position, range::Range};
+use crossterm::event::KeyCode;
+
+pub fn exec_search(app: &mut Application) -> Result<()> {
+    if let ModeData::Search(ref mut search_data) = app.mode {
+        search_data.is_exec_search = true;
+        if let Some(ref mut buffer) = app.workspace.current_buffer {
+            let search_string = search_data.search_string.clone();
+            let search_result = buffer.search(&search_string);
+
+            let fixed_offset = search_string.len();
+            let ranges: Vec<Range> = search_result
+                .into_iter()
+                .map(|pos| {
+                    let end_position = Position {
+                        line: pos.line,
+                        offset: pos.offset + fixed_offset,
+                    };
+                    Range::new(pos, end_position)
+                })
+                .collect();
+
+            search_data.search_result = ranges;
+        }
+    }
+    Ok(())
+}
+
+pub fn input_search_data(app: &mut Application) -> Result<()> {
+    if let Some(key) = app.monitor.last_key {
+        if let KeyCode::Char(c) = key.code {
+            if let ModeData::Search(ref mut search_data) = app.mode {
+                if search_data.is_exec_search == false {
+                    search_data
+                        .search_string
+                        .insert(search_data.search_string.len(), c);
+                }
+            }
+        }
+    }
+    Ok(())
+}
+
+pub fn backspace(app: &mut Application) -> Result<()> {
+    if let ModeData::Search(ref mut search_data) = app.mode {
+        if search_data.is_exec_search == false && search_data.search_string.len() > 0 {
+            search_data
+                .search_string
+                .remove(search_data.search_string.len() - 1);
+        }
+    }
+    Ok(())
+}
+
+pub fn last_result(app: &mut Application) -> Result<()> {
+    if let ModeData::Search(ref mut search_data) = app.mode {
+        if search_data.is_exec_search == true && search_data.search_result.len() != 1 {
+            search_data.search_result_index =
+                (search_data.search_result_index + search_data.search_result.len() - 2)
+                    % (search_data.search_result.len() - 1);
+        }
+    }
+    Ok(())
+}
+
+pub fn next_result(app: &mut Application) -> Result<()> {
+    if let ModeData::Search(ref mut search_data) = app.mode {
+        if search_data.is_exec_search == true && search_data.search_result.len() != 1 {
+            search_data.search_result_index =
+                (search_data.search_result_index + 1) % (search_data.search_result.len() - 1);
+        }
+    }
+    Ok(())
+}
+
+pub fn clear(app: &mut Application) -> Result<()> {
+    if let ModeData::Search(ref mut search_data) = app.mode {
+        search_data.clear();
+    }
+    Ok(())
+}

+ 3 - 1
src/application/mod.rs

@@ -6,7 +6,7 @@ use crate::{
 use crossterm::{event::Event, terminal::disable_raw_mode};
 use held_core::plugin::Plugin;
 use mode::{
-    error::ErrorRenderer, workspace::WorkspaceModeData, ModeData, ModeKey, ModeRenderer, ModeRouter,
+    error::ErrorRenderer, workspace::WorkspaceModeData, search::SearchData, ModeData, ModeKey, ModeRenderer, ModeRouter,
 };
 use smallvec::SmallVec;
 use state::ApplicationStateData;
@@ -117,6 +117,8 @@ impl Application {
         self.mode_history.insert(ModeKey::Delete, ModeData::Delete);
 
         Ok(())
+        self.mode_history
+            .insert(ModeKey::Search, ModeData::Search(SearchData::new()));
     }
 
     pub fn run(&mut self) -> Result<()> {

+ 6 - 0
src/application/mode/mod.rs

@@ -8,6 +8,7 @@ use error_chain::bail;
 use insert::InsertRenderer;
 use linked_hash_map::LinkedHashMap;
 use normal::NormalRenderer;
+use search::{SearchData, SearchRenderer};
 use smallvec::SmallVec;
 use strum::EnumIter;
 use workspace::{WorkspaceModeData, WorkspaceRender};
@@ -23,6 +24,7 @@ pub mod error;
 mod insert;
 pub mod normal;
 pub mod workspace;
+pub mod search;
 
 pub enum ModeData {
     Normal,
@@ -30,6 +32,7 @@ pub enum ModeData {
     Exit,
     Insert,
     Workspace(WorkspaceModeData),
+    Search(SearchData),
     Delete, // Other(OtherData)
 }
 
@@ -40,6 +43,7 @@ pub enum ModeKey {
     Exit,
     Insert,
     Workspace,
+    Search,
     Delete,
 }
 
@@ -49,6 +53,7 @@ impl ModeKey {
             ModeKey::Normal => Some("normal".into()),
             ModeKey::Insert => Some("insert".into()),
             ModeKey::Workspace => Some("workspace".into()),
+            ModeKey::Search => Some("search".into()),
             ModeKey::Delete => Some("delete".into()),
             _ => None,
         }
@@ -146,6 +151,7 @@ impl ModeRenderer for ModeRouter {
             ModeData::Error(_) => ErrorRenderer::render(workspace, monitor, mode),
             ModeData::Insert => InsertRenderer::render(workspace, monitor, mode),
             ModeData::Workspace(_) => WorkspaceRender::render(workspace, monitor, mode),
+            ModeData::Search(_) => SearchRenderer::render(workspace, monitor, mode),
             ModeData::Exit => todo!(),
             ModeData::Delete => DeleteRenderer::render(workspace, monitor, mode),
         }

+ 95 - 0
src/application/mode/search.rs

@@ -0,0 +1,95 @@
+use super::ModeRenderer;
+use crate::util::range::Range;
+use crate::{
+    errors::*,
+    view::{
+        colors::colors::Colors,
+        status_data::{buffer_status_data, StatusLineData},
+        style::CharStyle,
+    },
+};
+pub(super) struct SearchRenderer;
+
+impl ModeRenderer for SearchRenderer {
+    fn render(
+        workspace: &mut crate::workspace::Workspace,
+        monitor: &mut crate::view::monitor::Monitor,
+        _mode: &mut super::ModeData,
+    ) -> Result<()> {
+        let mut presenter = monitor.build_presenter()?;
+
+        if let Some(buffer) = &workspace.current_buffer {
+            let data = buffer.data();
+
+            if let super::ModeData::Search(ref search_data) = _mode {
+                let highlight_search_string = search_data.search_result.clone();
+
+                let highlight_search_string_slice: Option<&[Range]> =
+                    if !highlight_search_string.is_empty() {
+                        Some(
+                            &highlight_search_string[search_data.search_result_index
+                                ..search_data.search_result_index + 1],
+                        )
+                    } else {
+                        None
+                    };
+
+                presenter.print_buffer(
+                    buffer,
+                    &data,
+                    &workspace.syntax_set,
+                    highlight_search_string_slice,
+                    None,
+                )?;
+
+                let mode_name_data = StatusLineData {
+                    content: " Search/".to_string(),
+                    color: Colors::Inverted,
+                    style: CharStyle::Bold,
+                };
+
+                let search_data = StatusLineData {
+                    content: search_data.search_string.clone(),
+                    color: Colors::Default,
+                    style: CharStyle::Default,
+                };
+
+                presenter.print_status_line(&[
+                    mode_name_data,
+                    search_data,
+                    buffer_status_data(&workspace.current_buffer),
+                ])?;
+
+                presenter.present()?;
+            } else {
+            }
+        }
+        Ok(())
+    }
+}
+
+#[derive(Debug)]
+pub struct SearchData {
+    pub search_string: String,
+    pub is_exec_search: bool,
+    pub search_result_index: usize,
+    pub search_result: Vec<Range>,
+}
+
+impl SearchData {
+    pub fn new() -> Self {
+        Self {
+            search_string: String::new(),
+            is_exec_search: false,
+            search_result_index: 0,
+            search_result: Vec::new(),
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.search_string.clear();
+        self.is_exec_search = false;
+        self.search_result_index = 0;
+        self.search_result.clear();
+    }
+}

+ 15 - 1
src/modules/input/default.yaml

@@ -34,6 +34,7 @@ normal:
   l: normal::move_right_n
   d: app::to_delete_mode
   w: app::to_workspace_mode
+  /: app::to_search_mode
   n: normal::move_to_next_words
   b: normal::move_to_prev_words
   e: normal::move_to_next_words_end
@@ -63,6 +64,19 @@ workspace:
   enter: workspace::enter
   escape: workspace::to_normal_mode
   ctrl-c: app::exit
+search:
+  /:
+  - search::clear 
+  - app::to_search_mode
+  backspace: search::backspace
+  escape: 
+  - search::clear
+  - app::to_normal_mode
+  enter: search::exec_search
+  up: search::last_result
+  down: search::next_result
+  ctrl-c: app::exit
+  _:  search::input_search_data
 delete:
   ctrl-c: app::exit
   escape: app::to_normal_mode
@@ -72,4 +86,4 @@ delete:
   d: 
     - delete::delete_lines
     - cursor::move_to_start_of_line
-    - app::to_normal_mode
+    - app::to_normal_mode