re architecture

This commit is contained in:
Dongdong Zhou 2020-11-16 21:58:19 +00:00
parent bc0717a7e6
commit f4e11aee00
11 changed files with 426 additions and 34 deletions

14
Cargo.lock generated
View File

@ -1339,6 +1339,15 @@ dependencies = [
"libc",
]
[[package]]
name = "home"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "humantime"
version = "1.3.0"
@ -1610,6 +1619,7 @@ dependencies = [
"git2",
"include_dir",
"jsonrpc-lite",
"lapce-proxy",
"lazy_static 1.4.0",
"lsp-types 0.82.0",
"openssh",
@ -1627,6 +1637,7 @@ dependencies = [
"xi-core-lib",
"xi-rope",
"xi-rpc",
"xi-trace",
]
[[package]]
@ -1648,8 +1659,11 @@ dependencies = [
name = "lapce-proxy"
version = "0.0.1"
dependencies = [
"anyhow",
"home",
"serde",
"serde_json",
"toml",
"xi-rope",
"xi-rpc",
]

View File

@ -5,6 +5,8 @@ authors = ["Dongdong Zhou <dzhou121@gmail.com>"]
edition = "2018"
[dependencies]
lapce-proxy = { path = "../proxy" }
xi-trace = { path = "../../xi-editor/rust/trace/" }
git2 = "0.13"
openssh = "0.7.0"
tokio = { version = "0.3.3", features = ["full"] }

View File

@ -110,11 +110,21 @@ pub fn new(
event_sink: ExtEventSink,
sender: Sender<(WindowId, WidgetId, BufferId, u64)>,
) -> Buffer {
let rope = if let Ok(rope) = load_file(&window_id, &tab_id, path) {
rope
} else {
Rope::from("")
};
let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id);
let content = state
.proxy
.lock()
.as_ref()
.unwrap()
.new_buffer(buffer_id, PathBuf::from(path.to_string()))
.unwrap();
let rope = Rope::from_str(&content).unwrap();
// let rope = if let Ok(rope) = load_file(&window_id, &tab_id, path) {
// rope
// } else {
// Rope::from("")
// };
let mut parser = new_parser(LapceLanguage::Rust);
let tree = parser.parse(&rope.to_string(), None).unwrap();
@ -150,7 +160,6 @@ pub fn new(
let language_id = buffer.language_id.clone();
let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id);
state.plugins.lock().new_buffer(&PluginBufferInfo {
buffer_id: buffer_id.clone(),
language_id: buffer.language_id.clone(),

View File

@ -3050,7 +3050,8 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
};
let x = (last_line.to_string().len() - content.to_string().len())
as f64
* width;
* width
+ 10.0;
let content = content.to_string();
if let Some(text_layout) = self.text_layouts.get_mut(&content) {
if text_layout.text != content {
@ -3294,8 +3295,11 @@ fn paint_selection(
&Mode::Visual => (),
_ => return,
}
let start = buffer.offset_of_line(start_line);
let last_line = buffer.last_line();
if start_line > last_line {
return;
}
let start = buffer.offset_of_line(start_line);
let mut end_line = start_line + number_lines;
if end_line > last_line {
end_line = last_line;

View File

@ -25,15 +25,26 @@
};
use git2::Oid;
use git2::Repository;
use lapce_proxy::dispatch::NewBufferResponse;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::json;
use std::io::BufReader;
use std::path::Path;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{
collections::HashMap, fs::File, io::Read, path::PathBuf, str::FromStr,
sync::Arc, thread,
};
use toml;
use xi_rpc::Handler;
use xi_rpc::RpcLoop;
use xi_rpc::RpcPeer;
use xi_trace::enable_tracing;
lazy_static! {
//pub static ref LAPCE_STATE: LapceState = LapceState::new();
@ -290,30 +301,11 @@ pub struct LapceTabState {
pub plugins: Arc<Mutex<PluginCatalog>>,
pub lsp: Arc<Mutex<LspCatalog>>,
pub ssh_session: Arc<Mutex<Option<SshSession>>>,
pub proxy: Arc<Mutex<Option<LapceProxy>>>,
}
impl LapceTabState {
pub fn new(window_id: WindowId) -> LapceTabState {
let repo = Repository::open("/Users/Lulu/lapce").unwrap();
let head = repo.head().unwrap();
let tree = head.peel_to_tree().unwrap();
// let tree = repo.find_tree(oid).unwrap();
let tree_entry = tree.get_path(&PathBuf::from("Cargo.toml")).unwrap();
let blob = repo.find_blob(tree_entry.id()).unwrap();
let mut patch = git2::Patch::from_blob_and_buffer(
&blob,
None,
"lsjdf".as_bytes(),
None,
None,
)
.unwrap();
for i in 0..patch.num_hunks() {
let hunk = patch.hunk(i).unwrap();
println!("hunk {:?}", hunk);
}
println!("diff {}", patch.to_buf().unwrap().as_str().unwrap());
let workspace = LapceWorkspace {
kind: LapceWorkspaceType::Local,
path: PathBuf::from("/Users/Lulu/lapce"),
@ -352,7 +344,9 @@ pub fn new(window_id: WindowId) -> LapceTabState {
tab_id.clone(),
))),
ssh_session: Arc::new(Mutex::new(None)),
proxy: Arc::new(Mutex::new(None)),
};
start_proxy_process(window_id, tab_id);
let local_state = state.clone();
thread::spawn(move || {
local_state.start_plugin();
@ -582,6 +576,89 @@ pub fn set_container(&mut self, container: WidgetId) {
}
}
pub struct LapceProxy {
peer: RpcPeer,
process: Child,
}
impl LapceProxy {
pub fn new_buffer(&self, buffer_id: BufferId, path: PathBuf) -> Result<String> {
enable_tracing();
let result = self
.peer
.send_rpc_request(
"new_buffer",
&json!({ "buffer_id": buffer_id, "path": path }),
)
.map_err(|e| anyhow!("{:?}", e))?;
let resp: NewBufferResponse = serde_json::from_value(result)?;
return Ok(resp.content);
}
}
fn start_proxy_process(window_id: WindowId, tab_id: WidgetId) {
thread::spawn(move || {
let child = Command::new("/Users/Lulu/lapce/target/debug/lapce-proxy")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn();
if child.is_err() {
println!("can't start proxy {:?}", child);
return;
}
let mut child = child.unwrap();
let child_stdin = child.stdin.take().unwrap();
let child_stdout = child.stdout.take().unwrap();
let mut looper = RpcLoop::new(child_stdin);
let peer: RpcPeer = Box::new(looper.get_raw_peer());
let proxy = LapceProxy {
peer,
process: child,
};
{
let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id);
*state.proxy.lock() = Some(proxy);
}
let mut handler = ProxyHandler {};
if let Err(e) =
looper.mainloop(|| BufReader::new(child_stdout), &mut handler)
{
println!("proxy main loop failed {:?}", e);
}
println!("proxy main loop exit");
});
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Notification {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Request {}
pub struct ProxyHandler {}
impl Handler for ProxyHandler {
type Notification = Notification;
type Request = Request;
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<serde_json::Value, xi_rpc::RemoteError> {
Err(xi_rpc::RemoteError::InvalidRequest(None))
}
}
pub fn hex_to_color(hex: &str) -> Result<Color> {
let hex = hex.trim_start_matches("#");
let (r, g, b, a) = match hex.len() {

View File

@ -9,3 +9,6 @@ xi-rpc = { path = "../../xi-editor/rust/rpc/" }
xi-rope = { path = "../../xi-editor/rust/rope/" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.59"
anyhow = "1.0.32"
home = "0.5.3"
toml = "0.5.6"

View File

@ -1,3 +1,8 @@
use anyhow::Result;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use serde::{Deserialize, Deserializer, Serialize};
use xi_rope::{
interval::IntervalBounds, rope::Rope, Cursor, Delta, DeltaBuilder, Interval,
@ -10,4 +15,23 @@
pub struct Buffer {
pub id: BufferId,
pub rope: Rope,
pub path: PathBuf,
}
impl Buffer {
pub fn new(id: BufferId, path: PathBuf) -> Buffer {
let rope = if let Ok(rope) = load_file(&path) {
rope
} else {
Rope::from("")
};
Buffer { id, rope, path }
}
}
fn load_file(path: &PathBuf) -> Result<Rope> {
let mut f = File::open(path)?;
let mut bytes = Vec::new();
f.read_to_end(&mut bytes)?;
Ok(Rope::from(std::str::from_utf8(&bytes)?))
}

12
proxy/src/core_proxy.rs Normal file
View File

@ -0,0 +1,12 @@
use xi_rpc::RpcPeer;
#[derive(Clone)]
pub struct CoreProxy {
pub peer: RpcPeer,
}
impl CoreProxy {
pub fn new(peer: RpcPeer) -> Self {
Self { peer }
}
}

View File

@ -1,23 +1,47 @@
use crate::buffer::{Buffer, BufferId};
use crate::core_proxy::CoreProxy;
use crate::plugin::PluginCatalog;
use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use xi_rpc::RpcPeer;
use xi_rpc::{Handler, RpcCtx};
pub struct Dispatcher {
peer: RpcPeer,
buffers: HashMap<BufferId, Buffer>,
plugins: Arc<Mutex<PluginCatalog>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "method", content = "params")]
pub enum Notification {}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Request {}
#[serde(rename_all = "snake_case")]
#[serde(tag = "method", content = "params")]
pub enum Request {
NewBuffer { buffer_id: BufferId, path: PathBuf },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewBufferResponse {
pub content: String,
}
impl Dispatcher {
pub fn new() -> Dispatcher {
pub fn new(peer: RpcPeer) -> Dispatcher {
let mut plugins = PluginCatalog::new();
plugins.reload();
plugins.start_all(CoreProxy::new(peer.clone()));
Dispatcher {
peer,
buffers: HashMap::new(),
plugins: Arc::new(Mutex::new(plugins)),
}
}
}
@ -33,6 +57,15 @@ fn handle_request(
ctx: &RpcCtx,
rpc: Self::Request,
) -> Result<Value, xi_rpc::RemoteError> {
match rpc {
Request::NewBuffer { buffer_id, path } => {
let buffer = Buffer::new(buffer_id, path);
let content = buffer.rope.to_string();
self.buffers.insert(buffer_id, buffer);
let resp = NewBufferResponse { content };
return Ok(serde_json::to_value(resp).unwrap());
}
}
Err(xi_rpc::RemoteError::InvalidRequest(None))
}
}

View File

@ -1,16 +1,19 @@
pub mod buffer;
pub mod core_proxy;
pub mod dispatch;
pub mod plugin;
use dispatch::Dispatcher;
use xi_rpc::RpcLoop;
use std::io;
use xi_rpc::RpcLoop;
use xi_rpc::RpcPeer;
pub fn mainloop() {
let stdin = io::stdin();
let stdout = io::stdout();
let mut rpc_looper = RpcLoop::new(stdout);
let mut dispatcher = Dispatcher::new();
let peer: RpcPeer = Box::new(rpc_looper.get_raw_peer());
let mut dispatcher = Dispatcher::new(peer);
let result = rpc_looper.mainloop(|| stdin.lock(), &mut dispatcher);
eprintln!("rpc looper stopped {:?}", result);
}

View File

@ -1,30 +1,241 @@
use anyhow::Result;
use home::home_dir;
use serde::{Deserialize, Serialize};
use serde_json::json;
use serde_json::Value;
use std::collections::HashMap;
use std::fs;
use std::io::BufReader;
use std::io::Read;
use std::path::PathBuf;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use std::sync::Arc;
use std::thread;
use toml;
use xi_rpc::Handler;
use xi_rpc::RpcLoop;
use xi_rpc::RpcPeer;
use crate::buffer::BufferId;
use crate::core_proxy::CoreProxy;
pub type PluginName = String;
#[derive(Clone, Debug, Default)]
pub struct Counter(usize);
impl Counter {
pub fn next(&mut self) -> usize {
let n = self.0;
self.0 = n + 1;
n + 1
}
}
#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize)]
pub struct PluginId(pub usize);
#[derive(Deserialize, Clone)]
pub struct PluginDescription {
pub name: String,
pub version: String,
pub exec_path: PathBuf,
dir: PathBuf,
}
pub struct Plugin {
id: PluginId,
core: CoreProxy,
peer: RpcPeer,
name: String,
process: Child,
}
pub struct PluginCatalog {
id_counter: Counter,
items: HashMap<PluginName, PluginDescription>,
}
impl PluginCatalog {
pub fn new() -> PluginCatalog {
PluginCatalog {
id_counter: Counter::default(),
items: HashMap::new(),
}
}
pub fn reload(&mut self) {
println!("plugin reload from paths");
eprintln!("plugin reload from paths");
self.items.clear();
self.load();
}
pub fn load(&mut self) {}
pub fn load(&mut self) {
let all_manifests = find_all_manifests();
for manifest_path in &all_manifests {
match load_manifest(manifest_path) {
Err(e) => (),
Ok(manifest) => {
self.items.insert(manifest.name.clone(), manifest);
}
}
}
}
pub fn start_all(&mut self, core: CoreProxy) {
for (_, manifest) in self.items.clone().iter() {
start_plugin_process(
self.next_plugin_id(),
core.clone(),
manifest.clone(),
);
}
}
pub fn next_plugin_id(&mut self) -> PluginId {
PluginId(self.id_counter.next())
}
}
fn start_plugin_process(
id: PluginId,
core: CoreProxy,
plugin_desc: PluginDescription,
) {
thread::spawn(move || {
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);
let child = child.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn();
if child.is_err() {
eprintln!("can't start proxy {:?}", child);
return;
}
let mut child = child.unwrap();
let child_stdin = child.stdin.take().unwrap();
let child_stdout = child.stdout.take().unwrap();
let mut looper = RpcLoop::new(child_stdin);
let peer: RpcPeer = Box::new(looper.get_raw_peer());
let name = plugin_desc.name.clone();
let plugin = Plugin {
id,
core: core.clone(),
peer,
process: child,
name,
};
eprintln!("plugin main loop starting {:?}", &plugin_desc.exec_path);
plugin.initialize();
let mut handler = PluginHandler { core };
if let Err(e) =
looper.mainloop(|| BufReader::new(child_stdout), &mut handler)
{
eprintln!("plugin main loop failed {} {:?}", e, &plugin_desc.dir);
}
eprintln!("plugin main loop exit {:?}", plugin_desc.dir);
});
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "method", content = "params")]
pub enum PluginNotification {
StartLspServer {
exec_path: String,
language_id: String,
options: Option<Value>,
},
}
#[derive(Serialize, Deserialize, Debug)]
pub enum PluginRequest {}
pub struct PluginHandler {
core: CoreProxy,
}
impl Handler for PluginHandler {
type Notification = PluginNotification;
type Request = PluginRequest;
fn handle_notification(
&mut self,
ctx: &xi_rpc::RpcCtx,
rpc: Self::Notification,
) {
match &rpc {
PluginNotification::StartLspServer {
exec_path,
language_id,
options,
} => {
self.core.peer.send_rpc_notification(
"start_lsp_server",
&serde_json::to_value(&rpc).unwrap(),
);
}
}
}
fn handle_request(
&mut self,
ctx: &xi_rpc::RpcCtx,
rpc: Self::Request,
) -> Result<serde_json::Value, xi_rpc::RemoteError> {
Err(xi_rpc::RemoteError::InvalidRequest(None))
}
}
impl Plugin {
pub fn initialize(&self) {
self.peer.send_rpc_notification(
"initialize",
&json!({
"plugin_id": self.id,
}),
)
}
}
fn find_all_manifests() -> Vec<PathBuf> {
let mut manifest_paths = Vec::new();
let home = home_dir().unwrap();
let path = home.join(".lapce").join("plugins");
path.read_dir().map(|dir| {
dir.flat_map(|item| item.map(|p| p.path()).ok())
.map(|dir| dir.join("manifest.toml"))
.filter(|f| f.exists())
.for_each(|f| manifest_paths.push(f))
});
eprintln!("proxy mainfiest paths {:?}", manifest_paths);
manifest_paths
}
fn load_manifest(path: &PathBuf) -> Result<PluginDescription> {
let mut file = fs::File::open(&path)?;
let mut contents = String::new();
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.dir = path.parent().unwrap().canonicalize()?;
manifest.exec_path = path
.parent()
.unwrap()
.join(manifest.exec_path)
.canonicalize()?;
// }
Ok(manifest)
}