Browse Source

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

- 增加插件系统
- 更改部分项目结构
GnoCiYeH 5 months ago
parent
commit
6c9aadb8a1
56 changed files with 674 additions and 156 deletions
  1. 3 6
      Cargo.toml
  2. 1 0
      held_core/Cargo.toml
  3. 21 0
      held_core/src/interface/app.rs
  4. 7 0
      held_core/src/interface/buffer.rs
  5. 41 0
      held_core/src/interface/cursor.rs
  6. 24 0
      held_core/src/interface/mod.rs
  7. 5 0
      held_core/src/interface/monitor.rs
  8. 0 0
      held_core/src/interface/render.rs
  9. 4 0
      held_core/src/interface/terminal.rs
  10. 5 0
      held_core/src/interface/workspace.rs
  11. 25 5
      held_core/src/lib.rs
  12. 8 34
      held_core/src/plugin.rs
  13. 0 0
      held_core/src/utils/distance.rs
  14. 4 0
      held_core/src/utils/mod.rs
  15. 0 0
      held_core/src/utils/position.rs
  16. 1 1
      held_core/src/utils/range.rs
  17. 7 0
      held_core/src/utils/rectangle.rs
  18. 0 0
      held_core/src/view/colors.rs
  19. 3 0
      held_core/src/view/mod.rs
  20. 18 0
      held_core/src/view/render/cell.rs
  21. 56 0
      held_core/src/view/render/mod.rs
  22. 0 0
      held_core/src/view/style.rs
  23. 2 1
      src/application/handler/buffer.rs
  24. 2 1
      src/application/handler/insert.rs
  25. 21 7
      src/application/mod.rs
  26. 3 5
      src/application/mode/insert.rs
  27. 3 5
      src/application/mode/normal.rs
  28. 5 4
      src/application/mode/workspace.rs
  29. 17 0
      src/application/plugin_interafce/app.rs
  30. 17 0
      src/application/plugin_interafce/buffer.rs
  31. 29 0
      src/application/plugin_interafce/cursor.rs
  32. 11 0
      src/application/plugin_interafce/mod.rs
  33. 13 0
      src/application/plugin_interafce/monitor.rs
  34. 13 0
      src/application/plugin_interafce/workspace.rs
  35. 13 0
      src/application/state.rs
  36. 5 6
      src/buffer/gap_buffer.rs
  37. 2 2
      src/buffer/mod.rs
  38. 1 0
      src/errors.rs
  39. 13 1
      src/main.rs
  40. 12 0
      src/modules/perferences/mod.rs
  41. 43 1
      src/plugin/mod.rs
  42. 48 28
      src/plugin/system.rs
  43. 0 3
      src/util/mod.rs
  44. 2 1
      src/view/colors/map.rs
  45. 0 1
      src/view/colors/mod.rs
  46. 0 1
      src/view/mod.rs
  47. 7 2
      src/view/monitor/mod.rs
  48. 16 8
      src/view/presenter.rs
  49. 1 1
      src/view/render/lexeme_mapper.rs
  50. 5 5
      src/view/render/render_buffer.rs
  51. 52 5
      src/view/render/renderer.rs
  52. 2 2
      src/view/status_data.rs
  53. 17 17
      src/view/terminal/cross_terminal.rs
  54. 3 3
      src/view/terminal/mod.rs
  55. 10 0
      test/test_render_plugin/Cargo.toml
  56. 53 0
      test/test_render_plugin/src/lib.rs

+ 3 - 6
Cargo.toml

@@ -6,9 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [workspace]
-members = [
-    "held_core"
-]
+members = ["held_core", "test/test_render_plugin"]
 
 [features]
 dragonos = []
@@ -32,11 +30,9 @@ serde_yaml = "0.9"
 # 定义标志位
 bitflags = "2.4.2"
 
-# 加载插件
-libloading = "0.8.5"
 walkdir = "2.5.0"
 
-held_core = { path = "./held_core", version = "*" }
+held_core = { path = "./held_core" }
 unicode-segmentation = "1.12.0"
 syntect = "5.2.0"
 error-chain = "0.12.4"
@@ -45,6 +41,7 @@ app_dirs2 = "2.5.5"
 linked-hash-map = "0.5.6"
 strum = { version = "^0.26.3", features = ["std","derive"] }
 smallvec = "1.13.2"
+dlopen2 = "0.7.0"
 
 [build-dependencies]
 regex = "1.10"

+ 1 - 0
held_core/Cargo.toml

@@ -4,3 +4,4 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
+crossterm = "0.27"

+ 21 - 0
held_core/src/interface/app.rs

@@ -0,0 +1,21 @@
+use super::{get_application, APPLICATION};
+
+pub trait App {
+    fn exit(&mut self);
+
+    fn to_insert_mode(&mut self);
+
+    fn to_normal_mode(&mut self);
+}
+
+pub fn exit() {
+    get_application().exit();
+}
+
+pub fn to_insert_mode() {
+    get_application().to_insert_mode();
+}
+
+pub fn to_normal_mode() {
+    get_application().to_normal_mode();
+}

+ 7 - 0
held_core/src/interface/buffer.rs

@@ -0,0 +1,7 @@
+pub trait Buffer {
+    fn insert_char(&mut self);
+
+    fn new_line(&mut self);
+
+    fn insert_tab(&mut self);
+}

+ 41 - 0
held_core/src/interface/cursor.rs

@@ -0,0 +1,41 @@
+use crate::utils::position::Position;
+
+use super::get_application;
+
+pub trait Cursor {
+    fn move_left(&mut self);
+
+    fn move_right(&mut self);
+
+    fn move_up(&mut self);
+
+    fn move_down(&mut self);
+
+    fn move_to_start_of_line(&mut self);
+
+    fn screen_cursor_position(&self) -> Position;
+}
+
+pub fn screen_cursor_position() -> Position {
+    get_application().screen_cursor_position()
+}
+
+pub fn move_down() {
+    get_application().move_down()
+}
+
+pub fn move_up() {
+    get_application().move_up()
+}
+
+pub fn move_left() {
+    get_application().move_left()
+}
+
+pub fn move_right() {
+    get_application().move_right()
+}
+
+pub fn move_to_start_of_line() {
+    get_application().move_to_start_of_line()
+}

+ 24 - 0
held_core/src/interface/mod.rs

@@ -0,0 +1,24 @@
+use app::App;
+use buffer::Buffer;
+use cursor::Cursor;
+use monitor::Monitor;
+use workspace::Workspace;
+
+pub mod app;
+pub mod buffer;
+pub mod cursor;
+pub mod monitor;
+pub mod render;
+pub mod terminal;
+pub mod workspace;
+
+pub trait ApplicationInterface: App + Buffer + Cursor + Monitor + Workspace {}
+pub static mut APPLICATION: Option<&'static mut dyn ApplicationInterface> = None;
+
+pub(crate) fn get_application() -> &'static mut &'static mut dyn ApplicationInterface {
+    unsafe {
+        APPLICATION
+            .as_mut()
+            .expect("The application has not been initialized!")
+    }
+}

+ 5 - 0
held_core/src/interface/monitor.rs

@@ -0,0 +1,5 @@
+pub trait Monitor {
+    fn scroll_to_cursor(&mut self);
+
+    fn scroll_to_center(&mut self);
+}

+ 0 - 0
held_core/src/interface/render.rs


+ 4 - 0
held_core/src/interface/terminal.rs

@@ -0,0 +1,4 @@
+pub trait TerminalInfo {
+    fn width() -> usize;
+    fn height() -> usize;
+}

+ 5 - 0
held_core/src/interface/workspace.rs

@@ -0,0 +1,5 @@
+pub trait Workspace {
+    fn save_file(&mut self);
+
+    fn undo(&mut self);
+}

+ 25 - 5
held_core/src/lib.rs

@@ -1,10 +1,30 @@
-use plugin::{Plugin, PluginRegister};
-
 pub mod control;
+pub mod interface;
 pub mod plugin;
 pub mod theme;
+pub mod utils;
+pub mod view;
+
+#[macro_export]
+macro_rules! declare_plugin {
+    ($app:ty, $constructor:path) => {
+        use held_core::interface::ApplicationInterface;
+        use held_core::interface::APPLICATION;
+
+        #[no_mangle]
+        pub unsafe extern "C" fn init_plugin_application(
+            app: &'static mut dyn ApplicationInterface,
+        ) {
+            APPLICATION = Some(app);
+        }
 
-#[no_mangle]
-fn get_plugins() -> Vec<Box<dyn Plugin>> {
-    return PluginRegister::get_instance().consume();
+        #[no_mangle]
+        pub extern "C" fn plugin_create() -> *mut dyn $crate::plugin::Plugin {
+            // 确保构造器正确,所以做了这一步骤,来显示声明签名
+            let constructor: fn() -> $app = $constructor;
+            let object = constructor();
+            let boxed: Box<dyn $crate::plugin::Plugin> = Box::new(object);
+            Box::into_raw(boxed)
+        }
+    };
 }

+ 8 - 34
held_core/src/plugin.rs

@@ -1,40 +1,14 @@
-use std::sync::Mutex;
+use crate::view::render::ContentRenderBuffer;
 
-static MANAGER: PluginRegister = PluginRegister::new();
-
-pub struct PluginRegister {
-    plugins: Mutex<Vec<Box<dyn Plugin>>>,
-}
-
-unsafe impl Send for PluginRegister {}
-unsafe impl Sync for PluginRegister {}
+pub trait Plugin {
+    fn name(&self) -> &'static str;
 
-impl PluginRegister {
-    const fn new() -> Self {
-        Self {
-            plugins: Mutex::new(Vec::new()),
-        }
-    }
-    pub fn get_instance() -> &'static Self {
-        return &MANAGER;
-    }
+    fn init(&self);
 
-    pub fn register_plugin(&self, plugin: Box<dyn Plugin>) {
-        self.plugins.lock().unwrap().push(plugin);
-    }
+    fn deinit(&self);
 
-    pub(crate) fn consume(&self) -> Vec<Box<dyn Plugin>> {
-        let mut guard = self.plugins.lock().unwrap();
-        let mut ret = Vec::with_capacity(guard.len());
-        ret.append(&mut guard);
-        ret
+    // 渲染文本内容部分时会触发该回调,可以返回想要在content中渲染的buffer
+    fn on_render_content(&self) -> Vec<ContentRenderBuffer> {
+        vec![]
     }
 }
-
-pub trait Plugin {
-    fn name(&self) -> String;
-
-    fn init(&mut self);
-
-    fn deinit(&mut self);
-}

+ 0 - 0
src/util/distance.rs → held_core/src/utils/distance.rs


+ 4 - 0
held_core/src/utils/mod.rs

@@ -0,0 +1,4 @@
+pub mod distance;
+pub mod position;
+pub mod range;
+pub mod rectangle;

+ 0 - 0
src/util/position.rs → held_core/src/utils/position.rs


+ 1 - 1
src/util/range.rs → held_core/src/utils/range.rs

@@ -1,4 +1,4 @@
-use crate::util::position::Position;
+use crate::utils::position::Position;
 
 /// Range: 左开右闭区间
 #[derive(Clone, Debug, PartialEq, Default)]

+ 7 - 0
held_core/src/utils/rectangle.rs

@@ -0,0 +1,7 @@
+use super::position::Position;
+
+pub struct Rectangle {
+    pub position: Position,
+    pub width: usize,
+    pub height: usize,
+}

+ 0 - 0
src/view/colors/colors.rs → held_core/src/view/colors.rs


+ 3 - 0
held_core/src/view/mod.rs

@@ -0,0 +1,3 @@
+pub mod colors;
+pub mod render;
+pub mod style;

+ 18 - 0
held_core/src/view/render/cell.rs

@@ -0,0 +1,18 @@
+use crate::view::{colors::Colors, style::CharStyle};
+
+#[derive(Debug, Clone, PartialEq, Default)]
+pub struct Cell {
+    pub content: char,
+    pub colors: Colors,
+    pub style: CharStyle,
+}
+
+impl Cell {
+    pub fn new(content: char, colors: Colors, style: CharStyle) -> Cell {
+        Cell {
+            content,
+            colors,
+            style,
+        }
+    }
+}

+ 56 - 0
held_core/src/view/render/mod.rs

@@ -0,0 +1,56 @@
+use std::str::Chars;
+
+use cell::Cell;
+
+use crate::utils::{position::Position, rectangle::Rectangle};
+
+use super::{colors::Colors, style::CharStyle};
+
+pub mod cell;
+pub struct ContentRenderBuffer {
+    pub rectangle: Rectangle,
+    pub cells: Vec<Option<Cell>>,
+}
+
+impl ContentRenderBuffer {
+    pub fn new(rectangle: Rectangle) -> ContentRenderBuffer {
+        let cells = vec![None; rectangle.height * rectangle.width];
+        ContentRenderBuffer { rectangle, cells }
+    }
+
+    pub fn set_cell(&mut self, position: Position, cell: Option<Cell>) {
+        let index = position.line * self.rectangle.width + position.offset;
+        if index < self.cells.len() {
+            self.cells[index] = cell;
+        }
+    }
+
+    pub fn put_buffer(
+        &mut self,
+        position: Position,
+        buffer: String,
+        style: CharStyle,
+        colors: Colors,
+    ) {
+        let mut line = position.line;
+        let mut offset = position.offset;
+        for c in buffer.chars() {
+            let index = line * self.rectangle.width + offset;
+            if index < self.cells.len() {
+                let cell = Cell {
+                    content: c,
+                    colors,
+                    style,
+                };
+                self.cells[index] = Some(cell);
+                offset += 1;
+                if offset == self.rectangle.width {
+                    line += 1;
+                    offset = 0;
+                }
+            } else {
+                break;
+            }
+        }
+    }
+}

+ 0 - 0
src/view/style.rs → held_core/src/view/style.rs


+ 2 - 1
src/application/handler/buffer.rs

@@ -1,7 +1,8 @@
 use crossterm::event::KeyCode;
+use held_core::utils::position::Position;
 
+use crate::application::Application;
 use crate::errors::*;
-use crate::{application::Application, util::position::Position};
 
 use super::cursor;
 

+ 2 - 1
src/application/handler/insert.rs

@@ -1,6 +1,7 @@
+use held_core::utils::position::Position;
+
 use crate::application::Application;
 use crate::errors::*;
-use crate::util::position::Position;
 
 pub fn backspace(app: &mut Application) -> Result<()> {
     if let Some(ref mut buffer) = app.workspace.current_buffer {

+ 21 - 7
src/application/mod.rs

@@ -1,10 +1,15 @@
-use crate::errors::*;
-use crate::modules::input::{InputLoader, InputMapper};
+use crate::{
+    errors::*,
+    modules::input::{InputLoader, InputMapper},
+    plugin::system::PluginSystem,
+};
 use crossterm::{event::Event, terminal::disable_raw_mode};
+use held_core::plugin::Plugin;
 use mode::{
     error::ErrorRenderer, workspace::WorkspaceModeData, ModeData, ModeKey, ModeRenderer, ModeRouter,
 };
 use smallvec::SmallVec;
+use state::ApplicationStateData;
 
 use std::{cell::RefCell, collections::HashMap, mem, rc::Rc, sync::Arc};
 
@@ -18,6 +23,8 @@ use crate::{
 
 mod handler;
 pub mod mode;
+pub mod plugin_interafce;
+pub mod state;
 
 pub struct Application {
     file_manager: FileManager,
@@ -36,6 +43,8 @@ pub struct Application {
             SmallVec<[fn(&mut crate::Application) -> std::result::Result<(), Error>; 4]>,
         >,
     >,
+    plugin_system: Rc<RefCell<PluginSystem>>,
+    pub state_data: ApplicationStateData,
 }
 
 impl Application {
@@ -53,8 +62,13 @@ impl Application {
         let buf = file.init(bak)?;
 
         let perferences = PerferencesManager::load()?;
+
+        let plugin_system = Rc::new(RefCell::new(PluginSystem::init_system(
+            perferences.borrow().plugins_path()?,
+        )));
+
         let input_map = InputLoader::load(perferences.borrow().input_config_path()?)?;
-        let mut monitor = Monitor::new(perferences.clone())?;
+        let mut monitor = Monitor::new(perferences.clone(), plugin_system.clone())?;
         let workspace = Workspace::create_workspace(&mut monitor, perferences.borrow(), args)?;
         Ok(Self {
             file_manager: file,
@@ -67,6 +81,8 @@ impl Application {
             mode_key: ModeKey::Normal,
             mode_history: HashMap::new(),
             input_map,
+            plugin_system,
+            state_data: ApplicationStateData::default(),
         })
     }
 
@@ -75,6 +91,7 @@ impl Application {
         // PluginSystem::init_system();
         // self.monitor.terminal.clear().unwrap();
         self.init_modes()?;
+        self.plugin_system.borrow().init();
         // if !self.bak {
         //     self.ui.start_page_ui()?;
         // }
@@ -131,7 +148,7 @@ impl Application {
     }
 
     fn listen_event(&mut self) -> Result<()> {
-        let event = self.monitor.terminal.listen()?;
+        let event = self.monitor.listen()?;
         self.handle_input(event)?;
         Ok(())
     }
@@ -163,9 +180,6 @@ impl Application {
     }
 
     fn handle_input(&mut self, event: Event) -> Result<()> {
-        if let Event::Key(key_event) = event {
-            self.monitor.last_key = Some(key_event);
-        }
         let key = InputMapper::event_map_str(event);
         if key.is_none() {
             return Ok(());

+ 3 - 5
src/application/mode/insert.rs

@@ -1,8 +1,6 @@
-use crate::view::{
-    colors::colors::Colors,
-    status_data::{buffer_status_data, StatusLineData},
-    style::CharStyle,
-};
+use held_core::view::{colors::Colors, style::CharStyle};
+
+use crate::view::status_data::{buffer_status_data, StatusLineData};
 
 use super::ModeRenderer;
 

+ 3 - 5
src/application/mode/normal.rs

@@ -1,11 +1,9 @@
+use held_core::view::{colors::Colors, style::CharStyle};
+
 use super::ModeRenderer;
 use crate::{
     errors::*,
-    view::{
-        colors::colors::Colors,
-        status_data::{buffer_status_data, StatusLineData},
-        style::CharStyle,
-    },
+    view::status_data::{buffer_status_data, StatusLineData},
 };
 pub(super) struct NormalRenderer;
 

+ 5 - 4
src/application/mode/workspace.rs

@@ -2,6 +2,10 @@ use std::{collections::HashSet, os::unix::fs::MetadataExt, path::PathBuf};
 
 use crossterm::style::Color;
 use error_chain::bail;
+use held_core::{
+    utils::{position::Position, range::Range},
+    view::{colors::Colors, style::CharStyle},
+};
 use unicode_segmentation::UnicodeSegmentation;
 use walkdir::{DirEntry, DirEntryExt, WalkDir};
 
@@ -9,10 +13,7 @@ use super::{ModeData, ModeRenderer};
 use crate::{
     buffer::Buffer,
     errors::*,
-    util::{position::Position, range::Range},
-    view::{
-        colors::colors::Colors, monitor::Monitor, status_data::StatusLineData, style::CharStyle,
-    },
+    view::{monitor::Monitor, status_data::StatusLineData},
     workspace::Workspace,
 };
 pub struct WorkspaceModeData {

+ 17 - 0
src/application/plugin_interafce/app.rs

@@ -0,0 +1,17 @@
+use held_core::interface::app::App;
+
+use crate::application::Application;
+
+impl App for Application {
+    fn exit(&mut self) {
+        todo!()
+    }
+
+    fn to_insert_mode(&mut self) {
+        todo!()
+    }
+
+    fn to_normal_mode(&mut self) {
+        todo!()
+    }
+}

+ 17 - 0
src/application/plugin_interafce/buffer.rs

@@ -0,0 +1,17 @@
+use held_core::interface;
+
+use crate::application::Application;
+
+impl interface::buffer::Buffer for Application {
+    fn insert_char(&mut self) {
+        todo!()
+    }
+
+    fn new_line(&mut self) {
+        todo!()
+    }
+
+    fn insert_tab(&mut self) {
+        todo!()
+    }
+}

+ 29 - 0
src/application/plugin_interafce/cursor.rs

@@ -0,0 +1,29 @@
+use held_core::interface;
+
+use crate::application::Application;
+
+impl interface::cursor::Cursor for Application {
+    fn move_left(&mut self) {
+        todo!()
+    }
+
+    fn move_right(&mut self) {
+        todo!()
+    }
+
+    fn move_up(&mut self) {
+        todo!()
+    }
+
+    fn move_down(&mut self) {
+        todo!()
+    }
+
+    fn move_to_start_of_line(&mut self) {
+        todo!()
+    }
+
+    fn screen_cursor_position(&self) -> held_core::utils::position::Position {
+        self.state_data.cursor_state.screen_position
+    }
+}

+ 11 - 0
src/application/plugin_interafce/mod.rs

@@ -0,0 +1,11 @@
+use held_core::interface::{app::App, ApplicationInterface};
+
+use super::Application;
+
+pub mod app;
+pub mod buffer;
+pub mod cursor;
+pub mod monitor;
+pub mod workspace;
+
+impl ApplicationInterface for Application {}

+ 13 - 0
src/application/plugin_interafce/monitor.rs

@@ -0,0 +1,13 @@
+use held_core::interface;
+
+use crate::application::Application;
+
+impl interface::monitor::Monitor for Application {
+    fn scroll_to_cursor(&mut self) {
+        todo!()
+    }
+
+    fn scroll_to_center(&mut self) {
+        todo!()
+    }
+}

+ 13 - 0
src/application/plugin_interafce/workspace.rs

@@ -0,0 +1,13 @@
+use held_core::interface;
+
+use crate::application::Application;
+
+impl interface::workspace::Workspace for Application {
+    fn save_file(&mut self) {
+        todo!()
+    }
+
+    fn undo(&mut self) {
+        todo!()
+    }
+}

+ 13 - 0
src/application/state.rs

@@ -0,0 +1,13 @@
+use held_core::utils::position::Position;
+
+/// 用于记录当前实时更新的状态信息
+/// 因为某些插件可能需要获取实时的状态信息,所以所有实时的状态都可能需要更新到这里
+#[derive(Debug, Default)]
+pub struct ApplicationStateData {
+    pub cursor_state: CursorStateData,
+}
+
+#[derive(Debug, Default)]
+pub struct CursorStateData {
+    pub screen_position: Position,
+}

+ 5 - 6
src/buffer/gap_buffer.rs

@@ -1,9 +1,9 @@
 use std::{borrow::Borrow, fmt};
 
-use serde_yaml::Index;
+use held_core::utils::position::Position;
 use unicode_segmentation::UnicodeSegmentation;
 
-use crate::util::{position::Position, range::Range};
+use held_core::utils::range::Range;
 
 /// GapBuffer 增加减少重分配的buffer
 /// 在一整块buffer中有一段gap(空闲空间)将整块buffer分为两段,以实现插入删除等操作的高效率(减少重分配)
@@ -218,10 +218,9 @@ impl fmt::Display for GapBuffer {
 
 #[cfg(test)]
 mod tests {
-    use crate::{
-        buffer::{GapBuffer, Position},
-        util::range::Range,
-    };
+    use held_core::utils::range::Range;
+
+    use crate::buffer::{GapBuffer, Position};
 
     #[test]
     fn move_gap_works() {

+ 2 - 2
src/buffer/mod.rs

@@ -7,13 +7,13 @@ use std::rc::Rc;
 use std::{fs, io};
 
 use cursor::Cursor;
+use held_core::utils::position::Position;
 use operation::history::History;
 use operation::{Operation, OperationGroup};
 use syntect::parsing::SyntaxReference;
 
 use crate::errors::Error;
-use crate::util::position::Position;
-use crate::util::range::Range;
+use held_core::utils::range::Range;
 
 // Published API
 pub use self::gap_buffer::GapBuffer;

+ 1 - 0
src/errors.rs

@@ -35,5 +35,6 @@ error_chain! {
         ParsingError(syntect::parsing::ParsingError);
         ScopeError(syntect::parsing::ScopeError);
         SyntaxLoadingError(syntect::LoadingError);
+        DlopenError(dlopen2::Error);
     }
 }

+ 13 - 1
src/main.rs

@@ -24,6 +24,12 @@ extern crate simplelog;
 
 use crate::errors::*;
 
+pub static mut APPLICATION: Option<Application> = None;
+
+pub fn get_application() -> &'static mut Application {
+    unsafe { APPLICATION.as_mut().unwrap() }
+}
+
 fn main() -> Result<()> {
     let args: Vec<String> = env::args().collect();
     let config = CmdConfig::parse();
@@ -38,5 +44,11 @@ fn main() -> Result<()> {
         setting = serde_yaml::from_reader::<File, DeserializeAppOption>(file?).unwrap_or_default();
     }
 
-    Application::new(config.file, setting.to_app_setting(), &args)?.run()
+    let application = Application::new(config.file, setting.to_app_setting(), &args)?;
+
+    unsafe {
+        APPLICATION = Some(application);
+    }
+
+    get_application().run()
 }

+ 12 - 0
src/modules/perferences/mod.rs

@@ -15,6 +15,7 @@ pub mod yaml;
 const SYNTAX_PATH: &str = "syntaxes";
 const THEME_PATH: &str = "themes";
 const INPUT_CONFIG_PATH: &str = "input";
+const PLUGINS_PATH: &str = "plugins";
 const THEME_KET: &str = "theme";
 const LANGUAGE_KEY: &str = "language";
 const LANGUAGE_SYNTAX_KEY: &str = "syntax";
@@ -57,6 +58,17 @@ pub trait Perferences {
         Ok(PathBuf::new())
     }
 
+    // 插件路径
+    fn plugins_path(&self) -> Result<PathBuf> {
+        #[cfg(not(feature = "dragonos"))]
+        {
+            app_dir(AppDataType::UserConfig, &APP_INFO, PLUGINS_PATH)
+                .chain_err(|| "Couldn't create a themes directory or build a path tp it")
+        }
+        #[cfg(feature = "dragonos")]
+        Ok(PathBuf::new())
+    }
+
     // 设置的主题名字
     fn theme_name(&self) -> Option<String>;
 

+ 43 - 1
src/plugin/mod.rs

@@ -1 +1,43 @@
-pub(crate) mod system;
+use dlopen2::wrapper::{Container, WrapperApi};
+use held_core::{
+    interface::ApplicationInterface, plugin::Plugin, view::render::ContentRenderBuffer,
+};
+
+pub mod system;
+
+#[derive(WrapperApi)]
+pub struct PluginApi {
+    plugin_create: unsafe fn() -> *mut dyn Plugin,
+    init_plugin_application: unsafe fn(app: &'static mut dyn ApplicationInterface),
+}
+
+#[allow(dead_code)]
+pub struct PluginInstance {
+    // 顺序不能反,需要确保plugin在container之前销毁
+    plugin: Box<dyn Plugin>,
+    container: Container<PluginApi>,
+}
+
+impl PluginInstance {
+    pub fn new(plugin: Box<dyn Plugin>, container: Container<PluginApi>) -> PluginInstance {
+        PluginInstance { plugin, container }
+    }
+}
+
+impl Plugin for PluginInstance {
+    fn name(&self) -> &'static str {
+        self.plugin.name()
+    }
+
+    fn init(&self) {
+        self.plugin.init()
+    }
+
+    fn deinit(&self) {
+        self.plugin.deinit()
+    }
+
+    fn on_render_content(&self) -> Vec<ContentRenderBuffer> {
+        self.plugin.on_render_content()
+    }
+}

+ 48 - 28
src/plugin/system.rs

@@ -1,41 +1,39 @@
-use std::{path::PathBuf, sync::Mutex};
-
-use held_core::plugin::Plugin;
+use crate::{errors::*, get_application};
+use dlopen2::wrapper::Container;
+use held_core::{plugin::Plugin, view::render::ContentRenderBuffer};
+use std::{collections::HashMap, path::PathBuf, rc::Rc};
 use walkdir::WalkDir;
 
-pub static mut PULGIN_MANAGER: PluginSystem = PluginSystem::new();
+use crate::plugin::PluginApi;
+
+use super::PluginInstance;
 
 pub struct PluginSystem {
-    plugins: Mutex<Vec<Box<dyn Plugin>>>,
+    plugins: HashMap<&'static str, Rc<PluginInstance>>,
 }
 
 unsafe impl Send for PluginSystem {}
 unsafe impl Sync for PluginSystem {}
 
 impl PluginSystem {
-    const fn new() -> Self {
+    fn new() -> Self {
         Self {
-            plugins: Mutex::new(Vec::new()),
+            plugins: HashMap::new(),
         }
     }
 
-    // #[allow(static_mut_refs)]
-    // pub fn get_instance() -> &'static mut Self {
-    //     return unsafe { &mut PULGIN_MANAGER };
-    // }
-
-    pub fn init_system(pulgin_dir: PathBuf) {
-        unsafe {
-            PULGIN_MANAGER.load_pulgins(pulgin_dir);
-        };
+    pub fn init_system(pulgin_dir: PathBuf) -> PluginSystem {
+        let mut system = PluginSystem::new();
+        system.load_pulgins(pulgin_dir);
+        system
     }
 
-    pub unsafe fn load_pulgins(&mut self, pulgin_dir: PathBuf) {
+    pub fn load_pulgins(&mut self, pulgin_dir: PathBuf) {
         for entry in WalkDir::new(pulgin_dir) {
             if let Ok(entry) = entry {
                 if entry.file_type().is_file() {
                     let path = entry.into_path();
-                    if let Err(e) = self.load_pulgin(&path) {
+                    if let Err(e) = unsafe { self.load_pulgin(&path) } {
                         error!("load pulgin: {:?}, load error: {e:?}", path)
                     }
                 }
@@ -43,20 +41,42 @@ impl PluginSystem {
         }
     }
 
-    pub unsafe fn load_pulgin(&mut self, pulgin_path: &PathBuf) -> Result<(), libloading::Error> {
-        let lib = libloading::Library::new(pulgin_path)?;
-        let get_plugins: libloading::Symbol<extern "Rust" fn() -> Vec<Box<dyn Plugin>>> =
-            lib.get(b"get_plugins")?;
-
-        let mut plugins = get_plugins();
-        Self::init_plugins(&mut plugins);
-        self.plugins.lock().unwrap().append(&mut plugins);
+    pub unsafe fn load_pulgin(&mut self, pulgin_path: &PathBuf) -> Result<()> {
+        let container: Container<PluginApi> = Container::load(pulgin_path)?;
+        let plugin_raw = container.plugin_create();
+        let plugin = Box::from_raw(plugin_raw);
+        self.plugins.insert(
+            plugin.name(),
+            Rc::new(PluginInstance::new(plugin, container)),
+        );
         Ok(())
     }
+}
+
+impl Plugin for PluginSystem {
+    fn name(&self) -> &'static str {
+        ""
+    }
 
-    fn init_plugins(plugins: &mut Vec<Box<dyn Plugin>>) {
-        for plugin in plugins {
+    fn init(&self) {
+        for (_, plugin) in self.plugins.iter() {
+            unsafe { plugin.container.init_plugin_application(get_application()) };
             plugin.init();
         }
     }
+
+    fn deinit(&self) {
+        for (_, plugin) in self.plugins.iter() {
+            plugin.deinit();
+        }
+    }
+
+    fn on_render_content(&self) -> Vec<ContentRenderBuffer> {
+        let mut ret = vec![];
+        for (_, plugin) in self.plugins.iter() {
+            ret.append(&mut plugin.on_render_content());
+        }
+
+        ret
+    }
 }

+ 0 - 3
src/util/mod.rs

@@ -1,4 +1 @@
-pub mod distance;
 pub mod line_iterator;
-pub mod position;
-pub mod range;

+ 2 - 1
src/view/colors/map.rs

@@ -1,7 +1,8 @@
 use crossterm::style::Color;
+use held_core::view::colors::Colors;
 use syntect::highlighting::Theme;
 
-use super::{colors::Colors, to_rgb};
+use super::to_rgb;
 
 pub trait ColorMap {
     fn map_colors(&self, colors: Colors) -> Colors;

+ 0 - 1
src/view/colors/mod.rs

@@ -1,4 +1,3 @@
-pub mod colors;
 pub mod map;
 
 use crossterm::style::Color;

+ 0 - 1
src/view/mod.rs

@@ -3,6 +3,5 @@ pub mod monitor;
 pub mod presenter;
 pub mod render;
 pub mod status_data;
-pub mod style;
 pub mod terminal;
 pub mod theme_loadler;

+ 7 - 2
src/view/monitor/mod.rs

@@ -6,9 +6,9 @@ use super::{
     terminal::{cross_terminal::CrossTerminal, Terminal},
     theme_loadler::ThemeLoader,
 };
-use crate::buffer::Buffer;
 use crate::errors::*;
 use crate::modules::perferences::Perferences;
+use crate::{buffer::Buffer, plugin::system::PluginSystem};
 use crossterm::event::{Event, KeyEvent};
 use scroll_controller::ScrollController;
 use syntect::highlighting::{Theme, ThemeSet};
@@ -24,10 +24,14 @@ pub struct Monitor {
     render_caches: HashMap<usize, Rc<RefCell<HashMap<usize, RenderState>>>>,
     pub last_key: Option<KeyEvent>,
     pub cached_render_buffer: Rc<RefCell<CachedRenderBuffer>>,
+    pub plugin_system: Rc<RefCell<PluginSystem>>,
 }
 
 impl Monitor {
-    pub fn new(perference: Rc<RefCell<dyn Perferences>>) -> Result<Monitor> {
+    pub fn new(
+        perference: Rc<RefCell<dyn Perferences>>,
+        plugin_system: Rc<RefCell<PluginSystem>>,
+    ) -> Result<Monitor> {
         let terminal = CrossTerminal::new()?;
         let cached_render_buffer = CachedRenderBuffer::new(terminal.width()?, terminal.height()?);
         let theme_set = ThemeLoader::new(perference.borrow().theme_path()?).load()?;
@@ -39,6 +43,7 @@ impl Monitor {
             render_caches: HashMap::new(),
             last_key: None,
             cached_render_buffer: Rc::new(RefCell::new(cached_render_buffer)),
+            plugin_system,
         })
     }
 

+ 16 - 8
src/view/presenter.rs

@@ -1,20 +1,20 @@
-use std::{borrow::Cow, fmt::Debug};
+use std::{borrow::Cow, cell::RefCell, fmt::Debug, rc::Rc};
 
 use super::{
-    colors::{colors::Colors, map::ColorMap},
+    colors::map::ColorMap,
     monitor::Monitor,
     render::{
         lexeme_mapper::LexemeMapper,
         render_buffer::{Cell, RenderBuffer},
     },
     status_data::StatusLineData,
-    style::CharStyle,
 };
 use crate::{
-    buffer::Buffer,
-    errors::*,
-    util::{line_iterator::LineIterator, position::Position, range::Range},
-    view::render::renderer::Renderer,
+    buffer::Buffer, errors::*, util::line_iterator::LineIterator, view::render::renderer::Renderer,
+};
+use held_core::{
+    utils::{position::Position, range::Range},
+    view::{colors::Colors, style::CharStyle},
 };
 use syntect::{highlighting::Theme, parsing::SyntaxSet};
 
@@ -116,7 +116,8 @@ impl<'a> Presenter<'a> {
     ) -> Result<()> {
         let scroll_offset = self.view.get_scroll_controller(buffer).line_offset();
         let lines = LineIterator::new(&buffer_data);
-        self.cursor_position = Renderer::new(
+
+        let cursor_position = Renderer::new(
             buffer,
             &mut self.present_buffer,
             &**self.view.terminal,
@@ -126,8 +127,15 @@ impl<'a> Presenter<'a> {
             &self.theme,
             syntax_set,
             scroll_offset,
+            &mut self.view.plugin_system.borrow_mut(),
         )
         .render(lines, lexeme_mapper)?;
+
+        match cursor_position {
+            Some(position) => self.set_cursor(position),
+            None => self.cursor_position = None,
+        }
+
         Ok(())
     }
 

+ 1 - 1
src/view/render/lexeme_mapper.rs

@@ -1,4 +1,4 @@
-use crate::util::position::Position;
+use held_core::utils::position::Position;
 
 #[derive(Debug, PartialEq)]
 pub enum MappedLexeme<'a> {

+ 5 - 5
src/view/render/render_buffer.rs

@@ -1,11 +1,9 @@
 use std::{borrow::Cow, cell::RefCell, rc::Rc};
 
+use held_core::view::{colors::Colors, style::CharStyle};
 use unicode_segmentation::UnicodeSegmentation;
 
-use crate::{
-    util::position::Position,
-    view::{colors::colors::Colors, style::CharStyle},
-};
+use held_core::utils::position::Position;
 
 #[derive(Debug, Clone, PartialEq)]
 pub struct Cell<'a> {
@@ -102,8 +100,10 @@ impl<'a> RenderBuffer<'a> {
     }
 
     pub fn set_cell(&mut self, position: Position, cell: Cell<'a>) {
+        if position.line >= self.height || position.offset >= self.width {
+            return;
+        }
         let index = position.line * self.width + position.offset;
-
         if index < self.cells.len() {
             self.cells[index] = cell;
         }

+ 52 - 5
src/view/render/renderer.rs

@@ -4,15 +4,18 @@ use std::collections::HashMap;
 use std::rc::Rc;
 use std::str::FromStr;
 
-use crate::errors::*;
 use crate::modules::perferences::Perferences;
-use crate::util::position::Position;
-use crate::util::range::Range;
-use crate::view::colors::colors::Colors;
+use crate::plugin::system::PluginSystem;
 use crate::view::colors::to_rgb;
-use crate::view::style::CharStyle;
 use crate::{buffer::Buffer, util::line_iterator::LineIterator, view::terminal::Terminal};
+use crate::{errors::*, get_application};
 use crossterm::style::Color;
+use held_core::plugin::Plugin;
+use held_core::utils::position::Position;
+use held_core::utils::range::Range;
+use held_core::view::colors::Colors;
+use held_core::view::render::ContentRenderBuffer;
+use held_core::view::style::CharStyle;
 use syntect::highlighting::{HighlightIterator, Highlighter, Style, Theme};
 use syntect::parsing::{ScopeStack, SyntaxSet};
 
@@ -41,6 +44,7 @@ pub struct Renderer<'a, 'p> {
     cursor_position: Option<Position>,
     current_style: Style,
     perferences: &'a dyn Perferences,
+    plugin_system: &'a mut PluginSystem,
 }
 
 impl<'a, 'p> Renderer<'a, 'p> {
@@ -54,6 +58,7 @@ impl<'a, 'p> Renderer<'a, 'p> {
         theme: &'a Theme,
         syntax_set: &'a SyntaxSet,
         scroll_offset: usize,
+        plugin_system: &'a mut PluginSystem,
     ) -> Renderer<'a, 'p> {
         let line_number_iter = LineNumberStringIter::new(buffer, scroll_offset);
         let content_start_of_line = line_number_iter.width() + 1;
@@ -73,6 +78,7 @@ impl<'a, 'p> Renderer<'a, 'p> {
             highlight_ranges,
             line_number_iter,
             content_start_of_line,
+            plugin_system,
         }
     }
 
@@ -148,6 +154,8 @@ impl<'a, 'p> Renderer<'a, 'p> {
             self.try_to_advance_to_next_line(&line_data);
         }
 
+        self.render_plugins()?;
+
         Ok(self.cursor_position)
     }
 
@@ -159,6 +167,43 @@ impl<'a, 'p> Renderer<'a, 'p> {
         )
     }
 
+    fn render_plugins(&mut self) -> Result<()> {
+        let plugin_buffers = self.plugin_system.on_render_content();
+        for plugin_buffer in plugin_buffers {
+            self.render_plugin(plugin_buffer)?;
+        }
+        Ok(())
+    }
+
+    fn render_plugin(&mut self, buffer: ContentRenderBuffer) -> Result<()> {
+        let mut line = 0;
+        let mut offset = 0;
+        let init_pos = buffer.rectangle.position;
+
+        warn!("plugin cells {:?}", buffer.cells);
+        for cell in buffer.cells {
+            if let Some(cell) = cell {
+                self.render_cell(
+                    Position {
+                        line: init_pos.line + line,
+                        offset: init_pos.offset + offset,
+                    },
+                    cell.style,
+                    cell.colors,
+                    cell.content.to_string(),
+                );
+            }
+
+            offset += 1;
+            if offset == buffer.rectangle.width {
+                offset = 0;
+                line += 1;
+            }
+        }
+
+        Ok(())
+    }
+
     fn mapper_comment_style(highlighter: &Highlighter) -> Style {
         highlighter.style_for_stack(
             ScopeStack::from_str("keyword")
@@ -191,6 +236,7 @@ impl<'a, 'p> Renderer<'a, 'p> {
     fn set_cursor(&mut self) {
         if self.inside_visible() && *self.buffer.cursor == self.buffer_position {
             self.cursor_position = Some(self.screen_position);
+            get_application().state_data.cursor_state.screen_position = self.screen_position;
         }
     }
 
@@ -454,6 +500,7 @@ mod tests {
                 &theme,
                 &syntax_set,
                 0,
+                todo!(),
             );
             renderer.render(LineIterator::new(&binding), None).unwrap();
         }

+ 2 - 2
src/view/status_data.rs

@@ -1,6 +1,6 @@
-use crate::buffer::Buffer;
+use held_core::view::{colors::Colors, style::CharStyle};
 
-use super::{colors::colors::Colors, style::CharStyle};
+use crate::buffer::Buffer;
 
 pub struct StatusLineData {
     pub content: String,

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

@@ -8,9 +8,13 @@ use crossterm::{
     terminal::{self, disable_raw_mode},
     QueueableCommand,
 };
+use held_core::{
+    utils::position::Position,
+    view::{colors::Colors, style::CharStyle},
+};
 
 use super::{Terminal, MIN_HEIGHT, MIN_WIDTH, TERMINAL_EXECUTE_ERROR};
-use crate::{errors::*, util::position::Position};
+use crate::errors::*;
 
 #[derive(Debug)]
 pub struct CrossTerminal {
@@ -35,32 +39,28 @@ impl CrossTerminal {
         return self.ansi_buffer.borrow_mut();
     }
 
-    fn update_style(
-        &self,
-        char_style: crate::view::style::CharStyle,
-        colors: crate::view::colors::colors::Colors,
-    ) -> Result<()> {
+    fn update_style(&self, char_style: CharStyle, colors: Colors) -> Result<()> {
         self.buffer().queue(crossterm::style::SetAttribute(
             crossterm::style::Attribute::Reset,
         ))?;
 
         match char_style {
-            crate::view::style::CharStyle::Default => {
+            CharStyle::Default => {
                 self.buffer().queue(crossterm::style::SetAttribute(
                     crossterm::style::Attribute::Reset,
                 ))?;
             }
-            crate::view::style::CharStyle::Bold => {
+            CharStyle::Bold => {
                 self.buffer().queue(crossterm::style::SetAttribute(
                     crossterm::style::Attribute::Bold,
                 ))?;
             }
-            crate::view::style::CharStyle::Reverse => {
+            CharStyle::Reverse => {
                 self.buffer().queue(crossterm::style::SetAttribute(
                     crossterm::style::Attribute::Reverse,
                 ))?;
             }
-            crate::view::style::CharStyle::Italic => {
+            CharStyle::Italic => {
                 self.buffer().queue(crossterm::style::SetAttribute(
                     crossterm::style::Attribute::Italic,
                 ))?;
@@ -68,14 +68,14 @@ impl CrossTerminal {
         }
 
         match colors {
-            crate::view::colors::colors::Colors::Default => {
+            Colors::Default => {
                 self.buffer().queue(crossterm::style::ResetColor)?;
             }
-            crate::view::colors::colors::Colors::CustomForeground(color) => {
+            Colors::CustomForeground(color) => {
                 self.buffer()
                     .queue(crossterm::style::SetForegroundColor(color))?;
             }
-            crate::view::colors::colors::Colors::Custom(fg, bg) => {
+            Colors::Custom(fg, bg) => {
                 self.buffer()
                     .queue(crossterm::style::SetForegroundColor(fg))?;
                 self.buffer()
@@ -126,7 +126,7 @@ impl Terminal for CrossTerminal {
         return Ok(height.max(MIN_HEIGHT).into());
     }
 
-    fn set_cursor(&self, position: Option<crate::util::position::Position>) -> Result<()> {
+    fn set_cursor(&self, position: Option<Position>) -> Result<()> {
         match position {
             Some(position) => {
                 self.buffer()
@@ -157,9 +157,9 @@ impl Terminal for CrossTerminal {
 
     fn print(
         &self,
-        position: &crate::util::position::Position,
-        char_style: crate::view::style::CharStyle,
-        colors: crate::view::colors::colors::Colors,
+        position: &Position,
+        char_style: CharStyle,
+        colors: Colors,
         content: &str,
     ) -> Result<()> {
         self.update_style(char_style, colors)?;

+ 3 - 3
src/view/terminal/mod.rs

@@ -1,10 +1,10 @@
 use std::fmt::Debug;
 
 use crate::errors::*;
-use crate::util::position::Position;
 use crossterm::event::Event;
-
-use super::{colors::colors::Colors, style::CharStyle};
+use held_core::utils::position::Position;
+use held_core::view::colors::Colors;
+use held_core::view::style::CharStyle;
 
 pub mod cross_terminal;
 

+ 10 - 0
test/test_render_plugin/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "test_render_plugin"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["rlib", "dylib", "staticlib"]
+
+[dependencies]
+held_core = { path = "../../held_core" }

+ 53 - 0
test/test_render_plugin/src/lib.rs

@@ -0,0 +1,53 @@
+use held_core::{
+    declare_plugin, interface,
+    plugin::Plugin,
+    utils::{position::Position, rectangle::Rectangle},
+    view::{colors::Colors, render::ContentRenderBuffer, style::CharStyle},
+};
+
+declare_plugin!(RenderTestPlugin, RenderTestPlugin::new);
+
+struct RenderTestPlugin;
+
+impl RenderTestPlugin {
+    fn new() -> RenderTestPlugin {
+        RenderTestPlugin
+    }
+}
+
+impl Plugin for RenderTestPlugin {
+    fn name(&self) -> &'static str {
+        "render test plugin"
+    }
+
+    fn init(&self) {}
+
+    fn deinit(&self) {}
+
+    fn on_render_content(&self) -> Vec<ContentRenderBuffer> {
+        let cursor_position = interface::cursor::screen_cursor_position();
+
+        let width = 10;
+        let mut buffer = ContentRenderBuffer::new(Rectangle {
+            position: Position {
+                line: cursor_position.line.saturating_sub(1),
+                offset: cursor_position.offset - width / 2,
+            },
+            width,
+            height: 1,
+        });
+
+        let buffer_str = format!("{}:{}", cursor_position.offset, cursor_position.line);
+        buffer.put_buffer(
+            Position {
+                line: 0,
+                offset: (width - buffer_str.len()) / 2,
+            },
+            buffer_str,
+            CharStyle::Bold,
+            Colors::Warning,
+        );
+
+        vec![buffer]
+    }
+}