diff --git a/Cargo.lock b/Cargo.lock index eeb65e59..72b0c554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,9 +35,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" [[package]] name = "anymap" @@ -1491,6 +1491,28 @@ dependencies = [ "xi-rpc", ] +[[package]] +name = "lapce-core" +version = "0.0.1" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "toml", + "xi-rpc", +] + +[[package]] +name = "lapce-lsp" +version = "0.0.1" +dependencies = [ + "anyhow", + "lapce-core", + "serde", + "serde_json", + "xi-rpc", +] + [[package]] name = "lazy_static" version = "0.2.11" @@ -2523,9 +2545,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -2908,9 +2930,9 @@ checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" [[package]] name = "toml" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 25654799..ef24bf0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ anyhow = "1.0.32" strum = "0.19" strum_macros = "0.19" lazy_static = "1.4.0" -# piet-cairo = "0.0.6" serde = "1.0" serde_json = "1.0" syntect = "3.2" @@ -28,3 +27,6 @@ druid = { git = "https://github.com/linebender/druid", features = ["svg"] } [build-dependencies] cc = "*" + +[workspace] +members = ["lsp", "core"] diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..9eb472c7 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lapce-core" +version = "0.0.1" +authors = ["Dongdong Zhou "] +edition = "2018" + +[dependencies] +anyhow = "1.0.33" +serde = { version = "1.0", features = ["derive"] } +toml = "0.5.7" +xi-rpc = { path = "../../xi-editor/rust/rpc/" } +serde_json = "1.0.59" diff --git a/src/buffer.rs b/core/src/buffer.rs similarity index 100% rename from src/buffer.rs rename to core/src/buffer.rs diff --git a/src/command.rs b/core/src/command.rs similarity index 100% rename from src/command.rs rename to core/src/command.rs diff --git a/src/container.rs b/core/src/container.rs similarity index 98% rename from src/container.rs rename to core/src/container.rs index 0f1ebad1..359bcf19 100644 --- a/src/container.rs +++ b/core/src/container.rs @@ -43,8 +43,8 @@ pub struct LapceContainer { } impl LapceContainer { - pub fn new(state: LapceState) -> Self { - let palette = Palette::new(state.palette.lock().scroll_widget_id) + pub fn new() -> Self { + let palette = Palette::new(LAPCE_STATE.palette.lock().scroll_widget_id) .border(theme::BORDER_LIGHT, 1.0) .background(LapceTheme::PALETTE_BACKGROUND); let palette_id = WidgetId::next(); diff --git a/src/editor.rs b/core/src/editor.rs similarity index 99% rename from src/editor.rs rename to core/src/editor.rs index 50fe523f..1cbe8a8b 100644 --- a/src/editor.rs +++ b/core/src/editor.rs @@ -63,6 +63,7 @@ pub enum EditorOperator { #[derive(Clone)] pub struct EditorUIState { pub cursor: (usize, usize), + pub mode: Mode, pub visual_mode: VisualMode, pub selection: Selection, pub selection_start_line: usize, @@ -122,6 +123,7 @@ pub fn update( if editor.selection != old_editor.selection || editor.visual_mode != old_editor.visual_mode + || editor.mode != old_editor.mode { let rect = Rect::ZERO .with_origin(Point::new( @@ -681,6 +683,7 @@ impl EditorUIState { pub fn new() -> EditorUIState { EditorUIState { cursor: (0, 0), + mode: Mode::Normal, visual_mode: VisualMode::Normal, selection: Selection::new(), selection_start_line: 0, @@ -1356,6 +1359,7 @@ pub fn run_command( editor_ui_state.cursor = buffer.offset_to_line_col(editor.selection.get_cursor_offset()); editor_ui_state.visual_mode = self.visual_mode.clone(); + editor_ui_state.mode = self.mode.clone(); self.notify_fill_text_layouts(ctx, &buffer_id); None } @@ -1505,15 +1509,6 @@ fn event( LapceUICommand::RequestPaint => { ctx.request_paint(); } - LapceUICommand::EditorViewSize(size) => { - LAPCE_STATE - .editor_split - .lock() - .editors - .get_mut(&self.view_id) - .unwrap() - .view_size = *size; - } LapceUICommand::FillTextLayouts => { LAPCE_STATE.editor_split.lock().fill_text_layouts( ctx, @@ -1631,11 +1626,13 @@ fn lifecycle( ) { match event { LifeCycle::Size(size) => { - ctx.submit_command(Command::new( - LAPCE_UI_COMMAND, - LapceUICommand::EditorViewSize(*size), - Target::Widget(self.view_id.clone()), - )); + LAPCE_STATE + .editor_split + .lock() + .editors + .get_mut(&self.view_id) + .unwrap() + .view_size = *size; ctx.submit_command(Command::new( LAPCE_UI_COMMAND, LapceUICommand::FillTextLayouts, diff --git a/src/explorer.rs b/core/src/explorer.rs similarity index 100% rename from src/explorer.rs rename to core/src/explorer.rs diff --git a/src/keypress.rs b/core/src/keypress.rs similarity index 100% rename from src/keypress.rs rename to core/src/keypress.rs diff --git a/src/language.rs b/core/src/language.rs similarity index 100% rename from src/language.rs rename to core/src/language.rs diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..40e69f72 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,5 @@ +pub mod buffer; +pub mod command; +pub mod editor; +pub mod plugin; +pub mod state; diff --git a/src/movement.rs b/core/src/movement.rs similarity index 100% rename from src/movement.rs rename to core/src/movement.rs diff --git a/src/palette.rs b/core/src/palette.rs similarity index 100% rename from src/palette.rs rename to core/src/palette.rs diff --git a/src/plugin.rs b/core/src/plugin.rs similarity index 55% rename from src/plugin.rs rename to core/src/plugin.rs index db5239dd..c3c5a446 100644 --- a/src/plugin.rs +++ b/core/src/plugin.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use serde::{Deserialize, Deserializer, Serialize}; use std::{ collections::HashMap, @@ -12,9 +12,9 @@ thread, }; use toml; -use xi_rpc::{self, RpcLoop, RpcPeer}; +use xi_rpc::{self, Handler, RpcLoop, RpcPeer}; -use crate::{editor::Counter, state::LapceState}; +use crate::{editor::Counter, state::LAPCE_STATE}; pub type PluginName = String; @@ -25,13 +25,17 @@ pub struct PluginCatalog { items: HashMap>, locations: HashMap>, id_counter: Counter, + running: Vec, } +pub struct PluginHandler {} + #[derive(Deserialize)] pub struct PluginDescription { pub name: String, pub version: String, pub exec_path: PathBuf, + dir: Option, } pub struct Plugin { @@ -47,6 +51,7 @@ pub fn new() -> PluginCatalog { items: HashMap::new(), locations: HashMap::new(), id_counter: Counter::default(), + running: Vec::new(), } } @@ -73,6 +78,12 @@ pub fn load_from_paths(&mut self, paths: &[PathBuf]) { } } } + + pub fn start_all(&mut self) { + for (_, manifest) in self.items.clone().iter() { + start_plugin_process(manifest.clone(), self.next_plugin_id()); + } + } } fn find_all_manifests(paths: &[PathBuf]) -> Vec { @@ -91,6 +102,7 @@ fn find_all_manifests(paths: &[PathBuf]) -> Vec { .for_each(|f| manifest_paths.push(f)) }); } + println!("mainfiest paths {:?}", manifest_paths); manifest_paths } @@ -100,6 +112,7 @@ fn load_manifest(path: &Path) -> Result { file.read_to_string(&mut contents)?; let mut manifest: PluginDescription = toml::from_str(&contents)?; // normalize relative paths + manifest.dir = Some(path.parent().unwrap().canonicalize()?); if manifest.exec_path.starts_with("./") { manifest.exec_path = path .parent() @@ -110,17 +123,26 @@ fn load_manifest(path: &Path) -> Result { Ok(manifest) } -fn start_plugin_process( - plugin_desc: Arc, - id: PluginId, - state: LapceState, -) { +fn start_plugin_process(plugin_desc: Arc, id: PluginId) { thread::spawn(move || { - let child = Command::new(&plugin_desc.exec_path) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn(); - child.map(|mut child| { + println!( + "start plugin {:?} {:?}", + plugin_desc.exec_path, plugin_desc.dir + ); + let parts: Vec<&str> = plugin_desc + .exec_path + .to_str() + .unwrap() + .split(" ") + .into_iter() + .collect(); + let mut child = Command::new(parts[0]); + for part in &parts[1..] { + child.arg(part); + } + child.current_dir(plugin_desc.dir.as_ref().unwrap()); + let child = child.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn(); + if let Err(e) = child.map(|mut child| { let child_stdin = child.stdin.take().unwrap(); let child_stdout = child.stdout.take().unwrap(); let mut looper = RpcLoop::new(child_stdin); @@ -133,7 +155,55 @@ fn start_plugin_process( id, }; - // looper.mainloop(|| BufReader::new(child_stdout), handler); - }); + LAPCE_STATE.plugins.lock().running.push(plugin); + + let mut handler = PluginHandler {}; + if let Err(e) = + looper.mainloop(|| BufReader::new(child_stdout), &mut handler) + { + println!("plugin main loop failed {} {:?}", e, plugin_desc.dir); + } + }) { + println!( + "can't start plugin sub process {} {:?}", + e, plugin_desc.exec_path + ); + } }); } + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +/// RPC Notifications sent from the host +pub enum HostNotification {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +/// RPC Request sent from the host +pub enum HostRequest {} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum PluginNotification {} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum PluginRequest {} + +impl Handler for PluginHandler { + type Notification = PluginNotification; + type Request = PluginRequest; + + fn handle_notification( + &mut self, + ctx: &xi_rpc::RpcCtx, + rpc: Self::Notification, + ) { + } + + fn handle_request( + &mut self, + ctx: &xi_rpc::RpcCtx, + rpc: Self::Request, + ) -> Result { + Err(xi_rpc::RemoteError::InvalidRequest(None)) + } +} diff --git a/src/rpc.rs b/core/src/rpc.rs similarity index 100% rename from src/rpc.rs rename to core/src/rpc.rs diff --git a/src/scroll.rs b/core/src/scroll.rs similarity index 100% rename from src/scroll.rs rename to core/src/scroll.rs diff --git a/src/split.rs b/core/src/split.rs similarity index 93% rename from src/split.rs rename to core/src/split.rs index 200add62..c01109e6 100644 --- a/src/split.rs +++ b/core/src/split.rs @@ -214,6 +214,23 @@ fn event( selection.clone(), ); data.new_editor(&new_editor.view_id); + let editor_ui = data.get_editor(&active); + let selection = editor_ui.selection.clone(); + let visual_mode = editor_ui.visual_mode.clone(); + let mode = editor_ui.mode.clone(); + let selection_start_line = + editor_ui.selection_start_line; + let selection_end_line = + editor_ui.selection_end_line; + let new_editor_ui = + data.get_editor_mut(&new_editor.view_id); + new_editor_ui.selection = selection; + new_editor_ui.visual_mode = visual_mode; + new_editor_ui.mode = mode; + new_editor_ui.selection_start_line = + selection_start_line; + new_editor_ui.selection_end_line = + selection_end_line; let new_editor_view = EditorView::new( new_editor.split_id, diff --git a/src/state.rs b/core/src/state.rs similarity index 96% rename from src/state.rs rename to core/src/state.rs index 471e1c82..d0519bd5 100644 --- a/src/state.rs +++ b/core/src/state.rs @@ -12,6 +12,7 @@ keypress::KeyPressState, language::TreeSitter, palette::PaletteState, + plugin::PluginCatalog, }; use anyhow::{anyhow, Result}; use druid::{ @@ -21,7 +22,8 @@ use lazy_static::lazy_static; use parking_lot::Mutex; use std::{ - collections::HashMap, fs::File, io::Read, str::FromStr, sync::Arc, thread, + collections::HashMap, fs::File, io::Read, path::PathBuf, str::FromStr, + sync::Arc, thread, }; use toml; @@ -135,6 +137,7 @@ pub struct LapceState { pub editor_split: Arc>, pub container: Option, pub file_explorer: Arc>, + pub plugins: Arc>, } impl Data for LapceState { @@ -147,6 +150,9 @@ fn same(&self, other: &Self) -> bool { impl LapceState { pub fn new() -> LapceState { + let mut plugins = PluginCatalog::new(); + plugins.reload_from_paths(&[PathBuf::from_str("./lsp").unwrap()]); + plugins.start_all(); LapceState { theme: Self::get_theme().unwrap_or(HashMap::new()), focus: Arc::new(Mutex::new(LapceFocus::Editor)), @@ -155,6 +161,7 @@ pub fn new() -> LapceState { file_explorer: Arc::new(Mutex::new(FileExplorerState::new())), container: None, keypress: Arc::new(Mutex::new(KeyPressState::new())), + plugins: Arc::new(Mutex::new(plugins)), } } @@ -166,7 +173,6 @@ fn get_theme() -> Result> { let mut theme = HashMap::new(); for (name, hex) in toml_theme.iter() { - println!("{}", name); if let Ok(color) = hex_to_color(hex) { theme.insert(name.to_string(), color); } @@ -355,12 +361,6 @@ pub fn hex_to_color(hex: &str) -> Result { ), _ => return Err(anyhow!("invalid hex color")), }; - println!( - "{} {} {}", - u8::from_str_radix(&r, 16)?, - u8::from_str_radix(&g, 16)?, - u8::from_str_radix(&b, 16)? - ); Ok(Color::rgb8( u8::from_str_radix(&r, 16)?, u8::from_str_radix(&g, 16)?, diff --git a/src/theme.rs b/core/src/theme.rs similarity index 100% rename from src/theme.rs rename to core/src/theme.rs diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml new file mode 100644 index 00000000..e4f8e406 --- /dev/null +++ b/lsp/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lapce-lsp" +version = "0.0.1" +authors = ["Dongdong Zhou "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.59" +xi-rpc = { path = "../../xi-editor/rust/rpc/" } +lapce-core = { path = "../core" } +anyhow = "1.0.32" diff --git a/lsp/manifest.toml b/lsp/manifest.toml new file mode 100644 index 00000000..dcceb1f6 --- /dev/null +++ b/lsp/manifest.toml @@ -0,0 +1,3 @@ +name = "lapce-lsp-plugin" +version = "0.1" +exec_path = "cargo run --bin lapce-lsp-plugin" diff --git a/lsp/src/bin/lapce-lsp-plugin.rs b/lsp/src/bin/lapce-lsp-plugin.rs new file mode 100644 index 00000000..e726e592 --- /dev/null +++ b/lsp/src/bin/lapce-lsp-plugin.rs @@ -0,0 +1,5 @@ +use lapce_lsp::mainloop; + +fn main() { + mainloop(); +} diff --git a/lsp/src/dispatch.rs b/lsp/src/dispatch.rs new file mode 100644 index 00000000..99e1dd19 --- /dev/null +++ b/lsp/src/dispatch.rs @@ -0,0 +1,32 @@ +use lapce::plugin::{HostNotfication, HostRequest}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; +use xi_rpc::Handler; + +pub struct Dispatcher {} + +impl Dispatcher { + pub fn new() -> Dispatcher { + Dispatcher {} + } +} + +impl Handler for Dispatcher { + type Notification = HostNotification; + type Request = HostRequest; + + fn handle_notification( + &mut self, + ctx: &xi_rpc::RpcCtx, + rpc: Self::Notification, + ) { + } + + fn handle_request( + &mut self, + ctx: &xi_rpc::RpcCtx, + rpc: Self::Request, + ) -> Result { + Err(xi_rpc::RemoteError::InvalidRequest(None)) + } +} diff --git a/lsp/src/lib.rs b/lsp/src/lib.rs new file mode 100644 index 00000000..dd5a873b --- /dev/null +++ b/lsp/src/lib.rs @@ -0,0 +1,14 @@ +mod dispatch; + +use dispatch::Dispatcher; +use std::io; +use xi_rpc::RpcLoop; + +pub fn mainloop() { + let stdin = io::stdin(); + let stdout = io::stdout(); + let mut rpc_looper = RpcLoop::new(stdout); + let mut dispatcher = Dispatcher::new(); + + rpc_looper.mainloop(|| stdin.lock(), &mut dispatcher); +} diff --git a/src/main.rs b/src/main.rs index 0c568b35..1af2f517 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,10 +40,10 @@ fn tree_sitter_rust() -> Language; } -fn build_app(state: LapceState) -> impl Widget { +fn build_app() -> impl Widget { let container_id = WidgetId::next(); let container = - IdentityWrapper::wrap(LapceContainer::new(state), container_id.clone()); + IdentityWrapper::wrap(LapceContainer::new(), container_id.clone()); // LAPCE_STATE.set_container(container_id); let main_split = LapceSplit::new(true) .with_child(FileExplorer::new(), 300.0) @@ -127,9 +127,7 @@ pub fn main() { }); } // WindowDesc::new(|| LapceContainer::new()); - let state = LapceState::new(); - let init_state = state.clone(); - let window = WindowDesc::new(move || build_app(init_state)) + let window = WindowDesc::new(build_app) .title( LocalizedString::new("split-demo-window-title") .with_placeholder("Split Demo"),