Selaa lähdekoodia

feat-replace (#31)

* 增加Replace模式
---------

Co-authored-by: sparkzky <sparkhhhhhhhhh@outlook.com>
Co-authored-by: GnoCiYeH <heyicong@dragonos.org>
火花 7 kuukautta sitten
vanhempi
commit
78b3df08f5

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

@@ -52,3 +52,8 @@ pub fn to_delete_mode(app: &mut Application) -> Result<()> {
     app.switch_mode(ModeKey::Delete);
     Ok(())
 }
+
+pub fn to_replace_mode(app: &mut Application) -> Result<()> {
+    app.switch_mode(ModeKey::Replace);
+    Ok(())
+}

+ 14 - 0
src/application/handler/buffer.rs

@@ -16,6 +16,20 @@ pub fn insert_char(app: &mut Application) -> Result<()> {
     Ok(())
 }
 
+pub fn insert_char_on_replace(app: &mut Application) -> Result<()> {
+    if let Some(key) = app.monitor.last_key {
+        if let KeyCode::Char(c) = key.code {
+            app.workspace
+                .current_buffer
+                .as_mut()
+                .unwrap()
+                .replace_on_cursor(c.to_string());
+            cursor::move_right(app)?;
+        }
+    }
+    Ok(())
+}
+
 pub fn new_line(app: &mut Application) -> Result<()> {
     if let Some(ref mut buffer) = app.workspace.current_buffer {
         buffer.insert('\n');

+ 2 - 0
src/application/mod.rs

@@ -107,6 +107,8 @@ impl Application {
         self.mode_history.insert(ModeKey::Insert, ModeData::Insert);
         self.mode_history
             .insert(ModeKey::Command, ModeData::Command(CommandData::new()));
+        self.mode_history
+            .insert(ModeKey::Replace, ModeData::Replace);
         self.mode_history
             .insert(ModeKey::Error, ModeData::Error(Error::default()));
         self.mode_history.insert(ModeKey::Exit, ModeData::Exit);

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

@@ -10,6 +10,7 @@ use insert::InsertRenderer;
 use linked_hash_map::LinkedHashMap;
 use normal::NormalRenderer;
 use search::{SearchData, SearchRenderer};
+use replace::ReplaceRenderer;
 use smallvec::SmallVec;
 use strum::EnumIter;
 use workspace::{WorkspaceModeData, WorkspaceRender};
@@ -26,6 +27,7 @@ mod insert;
 pub mod normal;
 pub mod workspace;
 pub mod search;
+mod replace;
 
 pub enum ModeData {
     Normal,
@@ -35,7 +37,7 @@ pub enum ModeData {
     Command(CommandData),
     Workspace(WorkspaceModeData),
     Search(SearchData),
-    Delete, // Other(OtherData)
+    Delete, Replace, // Other(OtherData)
 }
 
 #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, EnumIter)]
@@ -48,6 +50,7 @@ pub enum ModeKey {
     Workspace,
     Search,
     Delete,
+    Replace,
 }
 
 impl ModeKey {
@@ -59,6 +62,7 @@ impl ModeKey {
             ModeKey::Workspace => Some("workspace".into()),
             ModeKey::Search => Some("search".into()),
             ModeKey::Delete => Some("delete".into()),
+            ModeKey::Replace => Some("replace".into()),
             _ => None,
         }
     }
@@ -157,6 +161,7 @@ impl ModeRenderer for ModeRouter {
             ModeData::Command(_) => CommandRenderer::render(workspace, monitor, mode),
             ModeData::Workspace(_) => WorkspaceRender::render(workspace, monitor, mode),
             ModeData::Search(_) => SearchRenderer::render(workspace, monitor, mode),
+            ModeData::Replace => ReplaceRenderer::render(workspace, monitor, mode),
             ModeData::Exit => todo!(),
             ModeData::Delete => DeleteRenderer::render(workspace, monitor, mode),
         }

+ 39 - 0
src/application/mode/replace.rs

@@ -0,0 +1,39 @@
+use crate::view::{
+    colors::colors::Colors,
+    status_data::{buffer_status_data, StatusLineData},
+    style::CharStyle,
+};
+
+use super::ModeRenderer;
+
+pub(super) struct ReplaceRenderer;
+
+impl ModeRenderer for ReplaceRenderer {
+    fn render(
+        workspace: &mut crate::workspace::Workspace,
+        monitor: &mut crate::view::monitor::Monitor,
+        _mode: &mut super::ModeData,
+    ) -> super::Result<()> {
+        let mut presenter = monitor.build_presenter()?;
+
+        if let Some(buffer) = &workspace.current_buffer {
+            let data = buffer.data();
+            presenter.print_buffer(buffer, &data, &workspace.syntax_set, None, None)?;
+
+            let mode_name_data = StatusLineData {
+                content: " REPLACE ".to_string(),
+                color: Colors::Inverted,
+                style: CharStyle::Bold,
+            };
+            presenter.print_status_line(&[
+                mode_name_data,
+                buffer_status_data(&workspace.current_buffer),
+            ])?;
+
+            presenter.present()?;
+        } else {
+        }
+
+        Ok(())
+    }
+}

+ 27 - 0
src/buffer/operation/replace.rs

@@ -82,6 +82,33 @@ impl Buffer {
             None => self.history.add(Box::new(op)),
         };
     }
+
+    pub fn replace_on_cursor<T: Into<String> + AsRef<str>>(&mut self, content: T) {
+        let old_content = self.data();
+        let offset = self.cursor.offset;
+        let line_number = self.cursor.line;
+        let lines: Vec<&str> = old_content.lines().collect();
+        let mut new_content = old_content.clone();
+
+        // 计算目标行的起始和结束位置
+        let mut line_start = 0;
+        let mut line_count = 0;
+
+        for (_, line) in lines.iter().enumerate() {
+            if line_count == line_number {
+                let line_end = line_start + offset;
+                if new_content.remove(line_end) == '\n' {
+                    new_content.insert(line_end, '\n');
+                }
+                new_content.insert_str(line_end, &content.into());
+                break;
+            }
+            line_start += line.len() + 1; // +1 是为了加上换行符的长度
+            line_count += 1;
+        }
+
+        self.replace(new_content);
+    }
 }
 
 fn replace_content(content: String, buffer: &mut Buffer) {

+ 19 - 0
src/modules/input/default.yaml

@@ -42,6 +42,7 @@ normal:
   u: buffer::undo
   ctrl-r: buffer::redo
   num: normal::count_cmd
+  shift-R: app::to_replace_mode
 insert:
   escape: app::to_normal_mode
   left: cursor::move_left
@@ -95,3 +96,21 @@ delete:
     - delete::delete_lines
     - cursor::move_to_start_of_line
     - app::to_normal_mode
+
+replace:
+  escape: app::to_normal_mode
+  left: cursor::move_left
+  right: cursor::move_right
+  up: cursor::move_up
+  down: cursor::move_down
+  ctrl-c: app::exit
+  ctrl-s: workspace::save_file
+  ctrl-z: workspace::undo
+  enter: 
+    - buffer::new_line
+    - cursor::move_down
+    - cursor::move_to_start_of_line
+  backspace: insert::backspace
+  tab: buffer::insert_tab
+  _: 
+    - buffer::insert_char_on_replace

+ 1 - 1
src/view/terminal/cross_terminal.rs

@@ -180,7 +180,7 @@ impl Terminal for CrossTerminal {
 
 impl Drop for CrossTerminal {
     fn drop(&mut self) {
-        // self.suspend();
+        self.suspend();
         let _ = disable_raw_mode();
     }
 }