Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions lapce-data/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,10 +289,12 @@ impl LapceEditorBufferData {
})
.collect::<Vec<(lapce_core::selection::Selection, &str)>>()
});
let additional_edit: Option<Vec<_>> =
additional_edit.as_ref().map(|edits| {
let additional_edit: Vec<_> = additional_edit
.as_ref()
.map(|edits| {
edits.iter().map(|(selection, c)| (selection, *c)).collect()
});
})
.unwrap_or_default();

let text_format = item
.insert_text_format
Expand All @@ -317,7 +319,7 @@ impl LapceEditorBufferData {
.do_raw_edit(
&[
&[(&selection, edit.new_text.as_str())][..],
&additional_edit.unwrap_or_default()[..],
&additional_edit[..],
]
.concat(),
lapce_core::editor::EditType::InsertChars,
Expand All @@ -340,7 +342,7 @@ impl LapceEditorBufferData {
.do_raw_edit(
&[
&[(&selection, text.as_str())][..],
&additional_edit.unwrap_or_default()[..],
&additional_edit[..],
]
.concat(),
lapce_core::editor::EditType::InsertChars,
Expand Down Expand Up @@ -397,7 +399,7 @@ impl LapceEditorBufferData {
&selection,
item.insert_text.as_deref().unwrap_or(item.label.as_str()),
)][..],
&additional_edit.unwrap_or_default()[..],
&additional_edit[..],
]
.concat(),
lapce_core::editor::EditType::InsertChars,
Expand Down
1 change: 1 addition & 0 deletions lapce-proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ home = "0.5.3"
toml = "0.5.6"
git2 = { version = "0.14.4", features = ["vendored-openssl"] }
lapce-rpc = { path = "../lapce-rpc" }
log = "0.4.17"
1 change: 1 addition & 0 deletions lapce-proxy/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ fn language_id_from_path(path: &Path) -> Option<&str> {
Some(match path.extension()?.to_str()? {
"rs" => "rust",
"go" => "go",
"py" => "python",
_ => return None,
})
}
Expand Down
95 changes: 89 additions & 6 deletions lapce-proxy/src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub struct LspState {
#[derive(Clone)]
pub struct LspClient {
exec_path: String,
args: Vec<String>,
options: Option<Value>,
state: Arc<Mutex<LspState>>,
dispatcher: Dispatcher,
Expand Down Expand Up @@ -84,15 +85,43 @@ impl LspCatalog {
language_id: &str,
options: Option<Value>,
) {
let args = self
.get_plugin_binary_args(options.clone())
.unwrap_or_default();
let client = LspClient::new(
language_id.to_string(),
exec_path,
options,
args,
self.dispatcher.clone().unwrap(),
);
self.clients.insert(language_id.to_string(), client);
}

fn get_plugin_binary_args(
&mut self,
option: Option<Value>,
) -> Option<Vec<String>> {
let option = option?;

match option["binary"].as_object()?.get("args")?.as_array() {
Some(options) => {
return Some(
options
.iter()
.filter_map(Value::as_str)
.map(ToString::to_string)
.collect::<Vec<_>>(),
)
}
None => {
log::warn!("args value should be of type [String].");
}
};

None
}

pub fn new_buffer(
&self,
buffer_id: &BufferId,
Expand Down Expand Up @@ -368,15 +397,18 @@ impl LspClient {
_language_id: String,
exec_path: &str,
options: Option<Value>,
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, 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(),
args,
options,
state: Arc::new(Mutex::new(LspState {
next_id: 0,
Expand Down Expand Up @@ -414,8 +446,11 @@ impl LspClient {
});
}

fn process(exec_path: &str) -> Child {
fn process(exec_path: &str, args: Vec<String>) -> Child {
let mut process = Command::new(exec_path);

process.args(args);

#[cfg(target_os = "windows")]
let process = process.creation_flags(0x08000000);
process
Expand All @@ -426,7 +461,8 @@ impl LspClient {
}

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.args.clone());
let writer = Box::new(BufWriter::new(process.stdin.take().unwrap()));
let stdout = process.stdout.take().unwrap();

Expand Down Expand Up @@ -474,8 +510,13 @@ impl LspClient {

pub fn handle_message(&self, message: &str) {
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(
Expand All @@ -497,6 +538,20 @@ impl LspClient {
}
}

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" => {
Expand All @@ -515,7 +570,19 @@ impl LspClient {
}),
);
}
_ => (),
"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);
}
}
}

Expand Down Expand Up @@ -564,6 +631,22 @@ impl LspClient {
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();
Expand Down
2 changes: 2 additions & 0 deletions lapce-proxy/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,12 @@ impl PluginCatalog {

let output = Pipe::new();
let input = Pipe::new();
let env = plugin_desc.get_plugin_env()?;
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)?;

Expand Down
77 changes: 76 additions & 1 deletion lapce-rpc/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -27,3 +28,77 @@ pub struct PluginInfo {
pub os: String,
pub configuration: Option<Value>,
}

impl PluginDescription {
pub fn get_plugin_env(&self) -> Result<Vec<(String, String)>, Error> {
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(vec![]),
};

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
))
}
};

Ok(data
.lines()
.filter_map(|l| l.split_once('='))
.map(|(k, v)| (k.into(), v.into()))
.collect::<Vec<(String, String)>>())
}
}