Browse Source

Feat Command (#29)

* Add command mode

* modified to resolve the conflict

* get command mode modified

* worksapce模式,支持查看工作区内容,动态打开工作区文件等操作 (#33)

* 将共用结构体移动到held_core中,增加部分插件相关的interface (#34)

- 增加插件系统
- 更改部分项目结构

---------

Co-authored-by: GnoCiYeH <heyicong@dragonos.org>
laokengwt 5 tháng trước cách đây
mục cha
commit
81f4a9154e

+ 22 - 1
src/application/handler/app.rs

@@ -1,4 +1,5 @@
-use crate::application::mode::ModeKey;
+use crate::application::mode::command::CommandData;
+use crate::application::mode::{ModeData, ModeKey};
 use crate::application::Application;
 use crate::errors::*;
 
@@ -7,6 +8,21 @@ pub fn exit(app: &mut Application) -> Result<()> {
     Ok(())
 }
 
+pub fn exit_with_check(app: &mut Application) -> Result<()> {
+    if let Some(ref buf) = app.workspace.current_buffer {
+        if buf.modified() {
+            // 输出提示(todo)
+            if let ModeData::Command(ref mut command_data) = app.mode {
+                command_data.reset();
+            }
+            app.switch_mode(ModeKey::Normal);
+        } else {
+            app.switch_mode(ModeKey::Exit);
+        }
+    }
+    Ok(())
+}
+
 pub fn to_insert_mode(app: &mut Application) -> Result<()> {
     app.switch_mode(ModeKey::Insert);
     Ok(())
@@ -22,6 +38,11 @@ pub fn to_workspace_mode(app: &mut Application) -> Result<()> {
     Ok(())
 }
 
+pub fn to_command_mode(app: &mut Application) -> Result<()> {
+    app.switch_mode(ModeKey::Command);
+    OK(())
+}
+
 pub fn to_search_mode(app: &mut Application) -> Result<()> {
     app.switch_mode(ModeKey::Search);
     OK(())

+ 106 - 0
src/application/handler/command.rs

@@ -0,0 +1,106 @@
+use std::collections::HashMap;
+
+use crossterm::event::KeyCode;
+use smallvec::SmallVec;
+
+use crate::application::handler::{app, workspace};
+use crate::application::mode::{ModeData, ModeKey};
+use crate::application::Application;
+use crate::errors::*;
+
+use lazy_static::lazy_static;
+
+lazy_static! {
+    static ref COMMAND: HashMap<String, SmallVec<[fn(&mut Application) -> Result<()>; 4]>> = {
+        let mut cmd_map =
+            HashMap::<String, SmallVec<[fn(&mut Application) -> Result<()>; 4]>>::new();
+
+        cmd_map.insert(
+            "q".to_string(),
+            SmallVec::from_vec(vec![
+                app::exit_with_check as fn(&mut Application) -> Result<()>,
+            ]),
+        );
+        cmd_map.insert(
+            "q!".to_string(),
+            SmallVec::from_vec(vec![app::exit as fn(&mut Application) -> Result<()>]),
+        );
+        cmd_map.insert(
+            "w".to_string(),
+            SmallVec::from_vec(vec![
+                workspace::save_file as fn(&mut Application) -> Result<()>,
+            ]),
+        );
+        cmd_map.insert(
+            "wq".to_string(),
+            SmallVec::from_vec(vec![
+                workspace::save_file as fn(&mut Application) -> Result<()>,
+                app::exit as fn(&mut Application) -> Result<()>,
+            ]),
+        );
+        cmd_map
+    };
+}
+
+pub fn commit_and_execute(app: &mut Application) -> Result<()> {
+    let cmd = match app.mode {
+        ModeData::Command(ref mut command_data) => command_data.input.clone(),
+        _ => String::new(),
+    };
+    // 匹配命令执行
+    match COMMAND.get(&cmd).cloned() {
+        Some(fucs) => {
+            for fuc in fucs {
+                fuc(app)?;
+            }
+            // 执行完函数清空数据
+            if let ModeData::Command(ref mut command_data) = app.mode {
+                command_data.reset();
+            }
+            if app.mode_key != ModeKey::Exit {
+                app::to_normal_mode(app)?;
+            }
+        }
+        None => {
+            if let ModeData::Command(ref mut command_data) = app.mode {
+                command_data.reset();
+            }
+            app::to_normal_mode(app)?;
+        }
+    }
+    // 匹配完reset
+    if let ModeData::Command(ref mut command_data) = app.mode {
+        command_data.reset();
+    }
+    Ok(())
+}
+
+pub fn insert_command(app: &mut Application) -> Result<()> {
+    if let Some(key) = app.monitor.last_key {
+        if let KeyCode::Char(c) = key.code {
+            if let ModeData::Command(ref mut command_data) = app.mode {
+                command_data.input.insert(command_data.input.len(), c);
+            }
+        }
+    }
+    Ok(())
+}
+
+pub fn backspace(app: &mut Application) -> Result<()> {
+    if let ModeData::Command(ref mut command_data) = app.mode {
+        if command_data.input.is_empty() {
+            return app::to_normal_mode(app);
+        } else {
+            command_data.input.remove(command_data.input.len() - 1);
+        }
+    }
+    Ok(())
+}
+
+pub fn to_normal_mode(app: &mut Application) -> Result<()> {
+    if let ModeData::Command(ref mut command_data) = app.mode {
+        command_data.reset();
+    }
+    app.switch_mode(ModeKey::Normal);
+    Ok(())
+}

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

@@ -4,6 +4,7 @@ use super::Application;
 use crate::errors::*;
 mod app;
 mod buffer;
+mod command;
 mod cursor;
 mod delete;
 mod insert;

+ 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, search::SearchData, ModeData, ModeKey, ModeRenderer, ModeRouter,
+    command::CommandData, error::ErrorRenderer, workspace::WorkspaceModeData, ModeData, ModeKey, search::SearchData, ModeRenderer, ModeRouter
 };
 use smallvec::SmallVec;
 use state::ApplicationStateData;
@@ -104,6 +104,8 @@ impl Application {
     fn init_modes(&mut self) -> Result<()> {
         self.mode_history.insert(ModeKey::Normal, ModeData::Normal);
         self.mode_history.insert(ModeKey::Insert, ModeData::Insert);
+        self.mode_history
+            .insert(ModeKey::Command, ModeData::Command(CommandData::new()));
         self.mode_history
             .insert(ModeKey::Error, ModeData::Error(Error::default()));
         self.mode_history.insert(ModeKey::Exit, ModeData::Exit);

+ 76 - 0
src/application/mode/command.rs

@@ -0,0 +1,76 @@
+use std::{collections::HashMap, process::CommandArgs};
+
+use held_core::{
+    utils::position::Position,
+    view::{colors::Colors, style::CharStyle},
+};
+
+use crate::view::status_data::StatusLineData;
+
+use super::{ModeData, ModeRenderer};
+
+const EDITED_NO_STORE: &'static str = "Changes have not been saved";
+const NOT_FOUNT_CMD: &'static str = "Command Not Fount";
+
+pub(super) struct CommandRenderer;
+
+impl ModeRenderer for CommandRenderer {
+    fn render(
+        workspace: &mut crate::workspace::Workspace,
+        monitor: &mut crate::view::monitor::Monitor,
+        mode: &mut super::ModeData,
+    ) -> super::Result<()> {
+        let line = monitor.height()? - 1;
+        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: " COMMAND ".to_string(),
+                color: Colors::Inverted,
+                style: CharStyle::Bold,
+            };
+
+            let cmd_str = if let ModeData::Command(command_data) = mode {
+                command_data.input.clone()
+            } else {
+                String::new()
+            };
+            let command_line_str = ":".to_owned() + &cmd_str;
+            let command_data = StatusLineData {
+                content: command_line_str.clone(),
+                color: Colors::Default,
+                style: CharStyle::Default,
+            };
+
+            presenter.print_status_line(&[mode_name_data, command_data])?;
+
+            let offset = " COMMAND ".len() + command_line_str.len();
+            presenter.set_cursor(Position { line, offset });
+
+            presenter.present()?;
+        } else {
+        }
+
+        Ok(())
+    }
+}
+
+#[derive(Debug)]
+pub struct CommandData {
+    pub input: String,
+}
+
+impl CommandData {
+    pub fn new() -> Self {
+        CommandData {
+            input: String::new(),
+        }
+    }
+
+    pub fn reset(&mut self) {
+        self.input.clear();
+    }
+}

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

@@ -2,6 +2,7 @@ use std::collections::HashMap;
 
 use crate::errors::*;
 use crate::{view::monitor::Monitor, workspace::Workspace};
+use command::{CommandData, CommandRenderer};
 use delete::DeleteRenderer;
 use error::ErrorRenderer;
 use error_chain::bail;
@@ -17,8 +18,8 @@ use yaml_rust::Yaml;
 use super::handler::handle_map;
 use super::Application;
 
+pub mod command;
 pub mod motion;
-
 pub mod delete;
 pub mod error;
 mod insert;
@@ -31,6 +32,7 @@ pub enum ModeData {
     Error(Error),
     Exit,
     Insert,
+    Command(CommandData),
     Workspace(WorkspaceModeData),
     Search(SearchData),
     Delete, // Other(OtherData)
@@ -42,6 +44,7 @@ pub enum ModeKey {
     Error,
     Exit,
     Insert,
+    Command,
     Workspace,
     Search,
     Delete,
@@ -52,6 +55,7 @@ impl ModeKey {
         match self {
             ModeKey::Normal => Some("normal".into()),
             ModeKey::Insert => Some("insert".into()),
+            ModeKey::Command => Some("command".into()),
             ModeKey::Workspace => Some("workspace".into()),
             ModeKey::Search => Some("search".into()),
             ModeKey::Delete => Some("delete".into()),
@@ -150,6 +154,7 @@ impl ModeRenderer for ModeRouter {
             ModeData::Normal => NormalRenderer::render(workspace, monitor, mode),
             ModeData::Error(_) => ErrorRenderer::render(workspace, monitor, mode),
             ModeData::Insert => InsertRenderer::render(workspace, monitor, mode),
+            ModeData::Command(_) => CommandRenderer::render(workspace, monitor, mode),
             ModeData::Workspace(_) => WorkspaceRender::render(workspace, monitor, mode),
             ModeData::Search(_) => SearchRenderer::render(workspace, monitor, mode),
             ModeData::Exit => todo!(),

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

@@ -5,6 +5,7 @@ normal:
   down: cursor::move_down
   ctrl-c: app::exit
   i: app::to_insert_mode
+  ':': app::to_command_mode
   a: 
     - app::to_insert_mode
     - cursor::move_right
@@ -58,6 +59,13 @@ insert:
   tab: buffer::insert_tab
   _: 
     - buffer::insert_char
+command:
+  escape: command::to_normal_mode
+  backspace: command::backspace
+  enter: command::commit_and_execute
+  _:
+    - command::insert_command
+
 workspace:
   up: workspace::move_up
   down: workspace::move_down