added main changes from Golang Support PR again

This commit is contained in:
Noé Heuillet 2022-05-25 17:21:34 -04:00
parent 9f0b0a67ea
commit 7881e86e9d
3 changed files with 184 additions and 7 deletions

View File

@ -57,6 +57,7 @@ pub struct LspState {
#[derive(Clone)]
pub struct LspClient {
exec_path: String,
binary_args: Vec<String>,
options: Option<Value>,
state: Arc<Mutex<LspState>>,
dispatcher: Dispatcher,
@ -84,15 +85,51 @@ pub fn start_server(
language_id: &str,
options: Option<Value>,
) {
let binary_args = self.get_plugin_binary_args(options.clone());
let client = LspClient::new(
language_id.to_string(),
exec_path,
options,
binary_args.to_owned(),
self.dispatcher.clone().unwrap(),
);
self.clients.insert(language_id.to_string(), client);
}
fn get_plugin_binary_args(&mut self, option: Option<Value>) -> Vec<String> {
let mut vals = Vec::new();
let option = match option {
Some(val) => val,
None => {
return vals;
}
};
let binary = match option["binary"].as_object() {
Some(binary) => binary,
None => return vals,
};
let option = match binary.get("binary_args") {
Some(binary_args) => binary_args,
None => return vals,
};
let option = if let Some(option) = option.as_array() {
option
} else {
println!("binary_args value should be of type [String].");
return vals;
};
for val in option {
vals.push(String::from(val.as_str().unwrap()));
}
return vals;
}
pub fn new_buffer(
&self,
buffer_id: &BufferId,
@ -368,15 +405,18 @@ pub fn new(
_language_id: String,
exec_path: &str,
options: Option<Value>,
binary_args: Vec<String>,
dispatcher: Dispatcher,
) -> Arc<LspClient> {
let mut process = Self::process(exec_path);
//TODO: better handling of binary args in plugin
let mut process = Self::process(exec_path, binary_args.clone());
let writer = Box::new(BufWriter::new(process.stdin.take().unwrap()));
let stdout = process.stdout.take().unwrap();
let lsp_client = Arc::new(LspClient {
dispatcher,
exec_path: exec_path.to_string(),
binary_args: binary_args,
options,
state: Arc::new(Mutex::new(LspState {
next_id: 0,
@ -414,8 +454,12 @@ fn handle_stdout(&self, stdout: ChildStdout) {
});
}
fn process(exec_path: &str) -> Child {
fn process(exec_path: &str, binary_args: Vec<String>) -> Child {
let mut process = Command::new(exec_path);
for arg in binary_args {
process.arg(arg);
}
#[cfg(target_os = "windows")]
let process = process.creation_flags(0x08000000);
process
@ -426,7 +470,8 @@ fn process(exec_path: &str) -> Child {
}
fn reload(&self) {
let mut process = Self::process(&self.exec_path);
//TODO: avoid clone using a &[String] ?
let mut process = Self::process(&self.exec_path, self.binary_args.clone());
let writer = Box::new(BufWriter::new(process.stdin.take().unwrap()));
let stdout = process.stdout.take().unwrap();
@ -473,9 +518,15 @@ pub fn get_uri(&self, buffer: &Buffer) -> Url {
}
pub fn handle_message(&self, message: &str) {
println!("Received message! {}", message);
match JsonRpc::parse(message) {
Ok(JsonRpc::Request(_obj)) => {
// trace!("client received unexpected request: {:?}", obj)
Ok(value @ JsonRpc::Request(_)) => {
let id = value.get_id().unwrap();
self.handle_request(
value.get_method().unwrap(),
id,
value.get_params().unwrap(),
)
}
Ok(value @ JsonRpc::Notification(_)) => {
self.handle_notification(
@ -497,6 +548,20 @@ pub fn handle_message(&self, message: &str) {
}
}
pub fn handle_request(&self, method: &str, id: Id, _params: Params) {
match method {
"window/workDoneProgress/create" => {
// Token is ignored as the workProgress Widget is always working
// In the future, for multiple workProgress Handling we should
// probably store the token
self.send_success_response(id, &json!({}));
}
method => {
println!("Received unhandled request {method}");
}
}
}
pub fn handle_notification(&self, method: &str, params: Params) {
match method {
"textDocument/publishDiagnostics" => {
@ -515,7 +580,19 @@ pub fn handle_notification(&self, method: &str, params: Params) {
}),
);
}
_ => (),
"window/showMessage" => {
// TODO: send message to display
}
"window/logMessage" => {
// TODO: We should log the message here. Waiting for
// the discussion about handling plugins logs before doing anything
}
"experimental/serverStatus" => {
//TODO: Logging of server status
}
method => {
println!("Received unhandled notification {}", method);
}
}
}
@ -564,6 +641,22 @@ pub fn send_request(&self, method: &str, params: Params, completion: Callback) {
self.send_rpc(&to_value(&request).unwrap());
}
pub fn send_success_response(&self, id: Id, result: &Value) {
let response = JsonRpc::success(id, result);
self.send_rpc(&to_value(&response).unwrap());
}
pub fn send_error_response(
&self,
id: jsonrpc_lite::Id,
error: jsonrpc_lite::Error,
) {
let response = JsonRpc::error(id, error);
self.send_rpc(&to_value(&response).unwrap());
}
fn initialize(&self) {
if let Some(workspace) = self.dispatcher.workspace.lock().clone() {
let root_url = Url::from_directory_path(workspace).unwrap();

View File

@ -171,10 +171,15 @@ fn start_plugin(
let output = Pipe::new();
let input = Pipe::new();
let env = match plugin_desc.get_plugin_env() {
Ok(env) => env,
Err(err) => return Err(err),
};
let mut wasi_env = WasiState::new("Lapce")
.map_dir("/", plugin_desc.dir.clone().unwrap())?
.stdin(Box::new(input))
.stdout(Box::new(output))
.envs(env)
.finalize()?;
let wasi = wasi_env.import_object(&module)?;

View File

@ -1,5 +1,6 @@
use std::path::PathBuf;
use std::{path::PathBuf, process::Command};
use anyhow::{format_err, Error};
use serde::{Deserialize, Serialize};
use serde_json::Value;
@ -27,3 +28,81 @@ pub struct PluginInfo {
pub os: String,
pub configuration: Option<Value>,
}
impl PluginDescription {
pub fn get_plugin_env(&self) -> Result<Vec<(String, String)>, Error> {
let mut vars = Vec::new();
let conf = match &self.configuration {
Some(val) => val,
None => {
return Err(format_err!(
"Empty configuration for plugin {}",
self.display_name
))
}
};
let conf = match conf.as_object() {
Some(val) => val,
None => {
return Err(format_err!(
"Empty configuration for plugin {}",
self.display_name
))
}
};
let env = match conf.get("env_command") {
Some(env) => env,
// We do not print any error as no env is allowed.
None => return Ok(vars),
};
let args = match env.as_str() {
Some(arg) => arg,
None => {
return Err(format_err!(
"Plugin {}: env_command is not a string",
self.display_name
))
}
};
let output = if cfg!(target_os = "windows") {
Command::new("cmd").arg("/c").arg(args).output()
} else {
Command::new("sh").arg("-c").arg(args).output()
};
let output = match output {
Ok(val) => val.stdout,
Err(err) => {
return Err(format_err!(
"Error during env command execution for plugin {}: {}",
self.name,
err
))
}
};
let data = match String::from_utf8(output) {
Ok(val) => val,
Err(err) => {
return Err(format_err!(
"Error during UTF-8 conversion for plugin {}: {}",
self.display_name,
err
))
}
};
for l in data.lines() {
if let Some((key, value)) = l.split_once('=') {
vars.push((String::from(key), String::from(value)));
};
}
return Ok(vars);
}
}